There really is no generic way to do this other than creating base generic repositories which I do not have time to provide you with code samples. Your best option is to have classes that the forms use to read and update your data so everything for CRUD is in one or more classes depending on how you decide to organize data.
Generic repositories start out with an interface such as below
public interface IBaseRepository<T> : IOperations<T> where T : class
{
}
public interface IOperations<T> where T : class
{
IEnumerable<T> GetAll();
Task<List<T>> GetAllAsync();
T GetById(int id);
T GetByIdWithIncludes(int id);
Task<T> GetByIdAsync(int id);
Task<T> GetByIdWithIncludesAsync(int id);
bool Remove(int id);
void Add(in T sender);
void Update(in T sender);
int Save();
Task<int> SaveAsync();
public T Select(Expression<Func<T, bool>> predicate);
public Task<T> SelectAsync(Expression<Func<T, bool>> predicate);
}
Than for each table you might have
public class ProductsRepository : IBaseRepository<Product>, IDisposable
{
private Context _context;
public ProductsRepository(Context context)
{
_context = context;
}
public IEnumerable<Product> GetAll()
{
return _context.Products.ToList();
}
public Task<List<Product>> GetAllAsync()
{
return _context.Products.ToListAsync();
}
public Product GetById(int id)
{
return _context.Products.Find(id);
}
public Product GetByIdWithIncludes(int id)
{
throw new NotImplementedException();
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
public Task<Product> GetByIdWithIncludesAsync(int id)
{
throw new NotImplementedException();
}
public bool Remove(int id)
{
var product = _context.Products.Find(id);
if (product is { })
{
_context.Products.Remove(product);
return true;
}
return false;
}
public void Add(in Product sender)
{
_context.Add(sender);
}
public void Update(in Product sender)
{
_context.Entry(sender).State = EntityState.Modified;
}
public int Save()
{
return _context.SaveChanges();
}
public Task<int> SaveAsync()
{
return _context.SaveChangesAsync();
}
public Product Select(Expression<Func<Product, bool>> predicate)
{
return _context.Products.WhereNullSafe(predicate).FirstOrDefault();
}
public async Task<Product> SelectAsync(Expression<Func<Product, bool>> predicate)
{
return await _context.Products.WhereNullSafe(predicate).FirstOrDefaultAsync();
}
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The above uses EF Core. Now we can go deeper and create a base core generic class project but if you are asking about generics than I suggest doing more research as at some level you may need to get into reflection.