1
\$\begingroup\$

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!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ But what is its use? Sure, you've implemented a paging solution, but that stops being useful once you need to add a filter (with one or more properties to filter on). And the rest is simply a layer on top of DbContext (which is already a repository). \$\endgroup\$
    – BCdotWEB
    Commented Dec 7, 2021 at 9:35
  • \$\begingroup\$ It's partially an attempt to ensure that there's some consistency going forward, I'm currently working with some morbid spaghetti code & there's quite literally 0 consistency whatsoever. P.S. It doesn't have to be a genius solution or anything, hence why I thought of exposing the Table property, allow more complex queries, e.g. filtering to be done at some other layer? ... \$\endgroup\$
    – JO3-W3B-D3V
    Commented Dec 7, 2021 at 9:46
  • \$\begingroup\$ DbSet it's already a repository, perhaps you need a Service or Manager pattern to group manage the business logic. Plus, the paging should have its own abstraction. \$\endgroup\$
    – iSR5
    Commented Dec 7, 2021 at 13:43
  • \$\begingroup\$ This is better done with composition. \$\endgroup\$
    – Anders
    Commented Dec 7, 2021 at 20:27
  • \$\begingroup\$ I would recommend against hard coding any database concept into the repository interface itself. You should be able to swap out the database with file storage without changing your code base much. The database concepts should be encapsulated to a single portion of the framework. \$\endgroup\$
    – Xtros
    Commented Dec 8, 2021 at 5:43

0