I'm currently in the process of defining some framework, one of the first things that I thought I could start with was defining some repository pattern/layer.
I generally wanted some input on it, because I still feel that there's room to make the code more concise than what it currently is, to be perfectly honest. 🤔
Any form of constructive criticism is more than welcome, I'll be perfectly honest, I'm by no means the most experienced .NET/C# guy around! 😅
Outline
I just wanted to make some 'framework' that allows one to create a repository with as little code as possible, so I'll cut right to the chase, it currently looks something like this:
IRepository
- This is the base interface, specifying the minimum number of methods that I think each repository implementation should have.AbstractRepository
- This is just some abstract class that implements some of the methods that I believe should be standard across the board.- Then there's the implementation of a repository.
Simple enough, so without waffling on, here's a taster of some of the code:
public interface IUniqueID<T> {
public T Id { get; set; }
}
public interface IRepository<DBType, IDType> where DBType : class, IUniqueID<IDType> {
DbSet<DBType> Table { get; }
DbContext DataContext { get; }
int Page { get; set; }
int PageSize { get; set; }
async Task<DBType> Get(IDType id) => await Table.FirstAsync(x => x.Id.Equals(id));
IRepository<DBType, IDType> WithPage(int page);
IRepository<DBType, IDType> WithPageSize(int pageSize);
async Task<DBType> Create(DBType entity) {
await DataContext.AddAsync(entity);
await DataContext.SaveChangesAsync();
return entity;
}
async Task<RepositoryPage> Read() {
var results = new RepositoryPage();
var page = (Page - 1) * PageSize;
results.Count = await Table.CountAsync();
results.Page = Page;
results.Results = Table.Skip(page).Take(PageSize);
return results;
}
async Task<DBType> Update(DBType entity) {
Table.Update(entity);
await DataContext.SaveChangesAsync();
return entity;
}
async Task Delete(DBType entity) {
Table.Remove(entity);
await DataContext.SaveChangesAsync();
}
public class RepositoryPage {
public int Page { get; set; }
public int Count { get; set; }
public IQueryable<DBType> Results { get; set; }
}
}
public class AbstractRepository<DBType, IDType> : IRepository<DBType, IDType> where DBType : class, IUniqueID<IDType> {
public int Page { get; set; }
public int PageSize { get; set; }
protected DataContext dataContext;
public DbContext DataContext => dataContext;
public DbSet<DBType> Table => throw new NotImplementedException();
public AbstractRepository(DataContext dataContext) {
this.dataContext = dataContext;
}
public IRepository<DBType, IDType> WithPage(int page) {
Page = page;
return this;
}
public IRepository<DBType, IDType> WithPageSize(int pageSize) {
PageSize = pageSize;
return this;
}
}
public interface IDocumentRepository: IRepository<DocumentEntity, int> {
}
public class DocumentRepository : AbstractRepository<DocumentEntity, int>, IDocumentRepository {
public DocumentRepository(DataContext dataContext) : base(dataContext) {
// Do nothing here.
}
public new DbSet<DocumentEntity> Table => dataContext.Documents;
}
Conclusion
So yeah, I appreciate that there is more than likely a much cleaner, minimal & easier way to do it & my peanut just ain't seeing it! 😅 - In my defence, I've got way more experience with Java & Node than .NET, but I'm happy to learn! 🙂
In all honesty, I wouldn't be surprised if one of you .NET/C# gurus out there showed me something like this that already exists in the wild & I'm simply oblivious to it!
Table
property, allow more complex queries, e.g. filtering to be done at some other layer? ... \$\endgroup\$DbSet
it's already a repository, perhaps you need aService
orManager
pattern to group manage the business logic. Plus, the paging should have its own abstraction. \$\endgroup\$