Skip to main content

Article No. 3. Genric Repository

A Truly Generic Repository, Part 1

This is part of a series on using generics in C# to make code more resuable. Other articles in this series:

The Problem

The code here is based on the ASP.NET tutorial, Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application. Probably like many new MVC developers, this was one of my first stops when learning the ropes, and I think it's probably done more disservice to the cause than any other article/tutorial out there. Sounds a bit harsh, I know, and mildly hypocritical since I opened by saying that I've based my code on it. Allow me to explain.
While there's some really good stuff there, a number of fatal flaws were also made. In short, the repository/unit of work patterns used in this way with Entity Framework is nothing short of idiotic. If you take a look at the implementations, the methods do little more than proxy to methods on DbContext. Then, the worst part is the unit of work itself, where you must write variations of the following bit of code for every single entity you want to work with.
private GenericRepository<Department> departmentRepository;

public GenericRepository<Department> DepartmentRepository
{
    get
    {
        if (this.departmentRepository == null)
        {
            this.departmentRepository = new GenericRepository<Department>(context);
        }
        return departmentRepository;
    }
}
That may not be too bad with the handful of entities they're working with in this sample project, but wait until you're dealing with 50, 100, 200 entities in a project. Suddenly, your UnitOfWork class is a rats' nest of brittle code that has to be maintained, and let's not even mention that virtually every bit of SOLID is violated in just this one seemingly innocent class.

So how is this different?

The following code diverges in a few major ways. First, and probably foremost, there's no UnitOfWork class. Instead, we're using a truly generic repository that can work with virtually any entity without having to new up multiple instances. Second, this implementation is provider agnostic. My example implementation is for Entity Framework, but this can be made to work with any store you have. Third, this solution is designed for dependency injection, which again, gives you the freedom to substitute implementations easily.

The Code

Without further ado...

Interfaces

First, we start off with two interfaces, IReadOnlyRepository and IRepository. As the names indicate, the former will contain only read methods while the latter will extend the first, adding write capability.
IReadOnlyRepository.cs
public interface IReadOnlyRepository
{
    IEnumerable<TEntity> GetAll<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    IEnumerable<TEntity> Get<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    Task<IEnumerable<TEntity>> GetAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity;

    TEntity GetOne<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    Task<TEntity> GetOneAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    TEntity GetFirst<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    Task<TEntity> GetFirstAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null)
        where TEntity : class, IEntity;

    TEntity GetById<TEntity>(object id)
        where TEntity : class, IEntity;

    Task<TEntity> GetByIdAsync<TEntity>(object id)
        where TEntity : class, IEntity;

    int GetCount<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    Task<int> GetCountAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    bool GetExists<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;

    Task<bool> GetExistsAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity;
}
IRepository.cs
public interface IRepository : IReadOnlyRepository
{
    void Create<TEntity>(TEntity entity, string createdBy = null)
        where TEntity : class, IEntity;

    void Update<TEntity>(TEntity entity, string modifiedBy = null)
        where TEntity : class, IEntity;

    void Delete<TEntity>(object id)
        where TEntity : class, IEntity;

    void Delete<TEntity>(TEntity entity)
        where TEntity : class, IEntity;

    void Save();

    Task SaveAsync();
}

Implementations

Now, we'll need classes that implement these interfaces. We're going to be creating specific implementations here based on Entity Framework, but you could conceivably have many similar implementations for things like a different ORM like NHibernate or even a Web API.
EntityFrameworkReadOnlyRepository.cs
public class EntityFrameworkReadOnlyRepository<TContext> : IReadOnlyRepository
    where TContext : DbContext
{
    protected readonly TContext context;

    public EntityFrameworkReadOnlyRepository(TContext context)
    {
        this.context = context;
    }

    protected virtual IQueryable<TEntity> GetQueryable<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity
    {
        includeProperties = includeProperties ?? string.Empty;
        IQueryable<TEntity> query = context.Set<TEntity>();

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            query = orderBy(query);
        }

        if (skip.HasValue)
        {
            query = query.Skip(skip.Value);
        }

        if (take.HasValue)
        {
            query = query.Take(take.Value);
        }

        return query;
    }

    public virtual IEnumerable<TEntity> GetAll<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(null, orderBy, includeProperties, skip, take).ToList();
    }

    public virtual async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity
    {
        return await GetQueryable<TEntity>(null, orderBy, includeProperties, skip, take).ToListAsync();
    }

    public virtual IEnumerable<TEntity> Get<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter, orderBy, includeProperties, skip, take).ToList();
    }

    public virtual async Task<IEnumerable<TEntity>> GetAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null,
        int? skip = null,
        int? take = null)
        where TEntity : class, IEntity
    {
        return await GetQueryable<TEntity>(filter, orderBy, includeProperties, skip, take).ToListAsync();
    }

    public virtual TEntity GetOne<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = "")
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter, null, includeProperties).SingleOrDefault();
    }

    public virtual async Task<TEntity> GetOneAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        string includeProperties = null)
        where TEntity : class, IEntity
    {
        return await GetQueryable<TEntity>(filter, null, includeProperties).SingleOrDefaultAsync();
    }

    public virtual TEntity GetFirst<TEntity>(
       Expression<Func<TEntity, bool>> filter = null,
       Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
       string includeProperties = "")
       where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter, orderBy, includeProperties).FirstOrDefault();
    }

    public virtual async Task<TEntity> GetFirstAsync<TEntity>(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = null)
        where TEntity : class, IEntity
    {
        return await GetQueryable<TEntity>(filter, orderBy, includeProperties).FirstOrDefaultAsync();
    }

    public virtual TEntity GetById<TEntity>(object id)
        where TEntity : class, IEntity
    {
        return context.Set<TEntity>().Find(id);
    }

    public virtual Task<TEntity> GetByIdAsync<TEntity>(object id)
        where TEntity : class, IEntity
    {
        return context.Set<TEntity>().FindAsync(id);
    }

    public virtual int GetCount<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter).Count();
    }

    public virtual Task<int> GetCountAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter).CountAsync();
    }

    public virtual bool GetExists<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter).Any();
    }

    public virtual Task<bool> GetExistsAsync<TEntity>(Expression<Func<TEntity, bool>> filter = null)
        where TEntity : class, IEntity
    {
        return GetQueryable<TEntity>(filter).AnyAsync();
    }
}
EntityFrameworkRepository.cs
public class EntityFrameworkRepository<TContext> : EntityFrameworkReadOnlyRepository<TContext>, IRepository
    where TContext : DbContext
{
    public EntityFrameworkRepository(TContext context)
        : base(context)
    {
    }

    public virtual void Create<TEntity>(TEntity entity, string createdBy = null)
        where TEntity : class, IEntity
    {
        entity.CreatedDate = DateTime.UtcNow;
        entity.CreatedBy = createdBy;
        context.Set<TEntity>().Add(entity);
    }

    public virtual void Update<TEntity>(TEntity entity, string modifiedBy = null)
        where TEntity : class, IEntity
    {
        entity.ModifiedDate = DateTime.UtcNow;
        entity.ModifiedBy = modifiedBy;
        context.Set<TEntity>().Attach(entity);
        context.Entry(entity).State = EntityState.Modified;
    }

    public virtual void Delete<TEntity>(object id)
        where TEntity : class, IEntity
    {
        TEntity entity = context.Set<TEntity>().Find(id);
        Delete(entity);
    }

    public virtual void Delete<TEntity>(TEntity entity)
        where TEntity : class, IEntity
    {
        var dbSet = context.Set<TEntity>();
        if (context.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dbSet.Remove(entity);
    }

    public virtual void Save()
    {
        try
        {
            context.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            ThrowEnhancedValidationException(e);
        }
    }

    public virtual Task SaveAsync()
    {
        try
        {
            return context.SaveChangesAsync();
        }
        catch (DbEntityValidationException e)
        {
            ThrowEnhancedValidationException(e);
        }

        return Task.FromResult(0);
    }

    protected virtual void ThrowEnhancedValidationException(DbEntityValidationException e)
    {
        var errorMessages = e.EntityValidationErrors
                .SelectMany(x => x.ValidationErrors)
                .Select(x => x.ErrorMessage);

        var fullErrorMessage = string.Join("; ", errorMessages);
        var exceptionMessage = string.Concat(e.Message, " The validation errors are: ", fullErrorMessage);
        throw new DbEntityValidationException(exceptionMessage, e.EntityValidationErrors);
    }
}

Usage

Here, I'm providing sample code based on using Ninject as a DI container for two reasons: 1) it's a very nice dependency injection solution that combines both flexibility and simplicity and 2) it's the DI container I'm familiar with. If you have a different preference it should be easy enough to adapt the code appropriately.
FooController.cs
public class FooController : Controller
{
    protected readonly IRepository repo;

    public FooController(IRepository repo)
    {
        this.repo = repo;
    }

    ...
}
NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ApplicationDbContext>()
          .ToSelf()
          .InRequestScope();
    kernel.Bind<IRepository>()
          .To<EntityFrameworkRepository<ApplicationDbContext>>()
          .InRequestScope();
}

A Truly Generic Repository, Part 2

This is part of a series on using generics in C# to make code more resuable. Other articles in this series:
In the first part of this article, we looked at creating a truly generic repository that can work with any entity without having to new up multiple instances. In this second part, we'll discuss how we can extend this to generically cover additional scenarios, as well as what to do when you truly need some method specifically targeting a single entity.
If you read the first part of this series, Generic Entity Base Class, you'll recall the PublicationEntity<T> class I gave there. If you haven't read that, yet, this might be a good time to give it a look. Simply, PublicationEntity<T> extended our Entity<T> class, adding some properties that deal with the "published" state of an entity: StatusPublishDate, and ExpireDate. It also implemented an interface, IPublicationEntity, which will be important to our discussion here.

Generics 101

I kind of threw you head-first into the world of generics, without much of a paddle. As a result, it's probably best if I back up a bit and explain a little about how they work. Basically, generics are kind of like a placeholder. You're telling the compiler, "I know I'll need some type here, but I'm not sure what that is right now. Once I do know, I'll let you know, as well." Going back to our Entity<T>example, we had the following property:
public T Id { get; set; }
What we're saying here is that we don't know right now what T is, so we'll pass it in later, when we do. The way we do that is with the angle brackets syntax. These are called our type parametersEntity<T> has a single type parameter, T, so in order to instantiate Entity<T> we'll have to pass something for that. If I were do something like the following in my application:
var entity = new Entity<int>();

// Note: Entity<T> is abstract, so this doesn't actually work.
// It's just for illustration.
The Id property becomes as if we statically typed it as:
public int Id { get; set; }
An important concept of generics is constraints. Constraints allow us to limit the parameter to a subset of types. You add a constraint, using where. You saw this in the code in part one. For example:
public TEntity FindById(object id)
    where TEntity : class, IEntity
This tells the compiler that the TEntity type parameter must be a class and it must have the IEntity interface. As a result, an exception will now be raised if you pass something like int, since that's a struct, not a class. You would also get an exception if you passed a class that doesn't implement IEntity. However, more important than telling the compiler what cannot be used as a type parameter, it tells the compiler what features the generic type will have. We can now freely interact with anything defined on IEntity within the method, because we know that any instance of TEntity will be something that implements IEntity.

Extending the Repository with Interfaces

Therefore, given our IPublicationEntity interface, we can create methods that specifically interact with properties any class that implements IPublicationEntity are guaranteed to have. For example:
TEntity GetOneLive<TEntity>(
    Expression<Func<TEntity, bool>> filter = null,
    string includeProperties = null)
    where TEntity : class, IPublicationEntity;
You'll notice this signature looks exactly like GetOne<TEntity> from IReadOnlyRepository, except that we added "Live" to the name and used IPublicationEntity as a constraint, rather than IEntity. Since, we're guaranteed to be working with entities that implement IPublicationEntity in this method, we can freely utilize the properties on that interface, StatusPublishDate, and ExpireDate, within the method. Let's look at the implementation:
protected virtual IQueryable<TEntity> GetLiveQueryable<TEntity>(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = null,
    int? skip = null,
    int? take = null)
    where TEntity : class, IPublicationEntity
{
    var query = GetQueryable<TEntity>(filter, orderBy, includeProperties);

    query = query.Where(m => m.Status == PublishStatus.Published &&
                             m.PublishDate.HasValue &&
                             m.PublishDate <= DateTime.UtcNow &&
                             (!m.ExpireDate.HasValue || m.ExpireDate > DateTime.UtcNow));

    if (skip.HasValue)
    {
        query = query.Skip(skip.Value);
    }

    if (take.HasValue)
    {
        query = query.Take(take.Value);
    }

    return query;
}

...

public virtual TEntity GetOneLive<TEntity>(
    Expression<Func<TEntity, bool>> filter = null,
    string includeProperties = null)
    where TEntity : class, IPublicationEntity
{
    return GetLiveQueryable<TEntity>(filter, null, includeProperties).SingleOrDefault();
}
First, I've added a new protected method, GetLiveQueryable. This is similar in function to the previous GetQueryable method we had already. In fact, it uses this method internally. However, it goes further by adding an additional where clause that defines what we consider "live": status set to "Published", publish date that's in the past, and either no expire date or a future expire date.
You'll notice that although I could have passed the skip and take parameters to GetQueryable as well, I've instead repeated the code from GetQueryable for that, here. The reason for that is simple. Skip and Take will directly act on our queryable. If we apply the "live" logic afterward, it will take effect after it's already been limited. To ensure that we skip/take the appropriate amount of items, we need to call that after all the where clause is done.
The GetOneLive method, then, is remarkably similar to GetOne: the only difference being that it calls GetLiveQueryable rather than GetQueryable. So, all we're doing is adding an additional layer of logic here, which of course is only possible because of the IPublicationEntity constraint. You can then rinse and repeat for all the other methods in the repository, creating "Live" versions of each.
Through the use of interfaces, generics and smart type parameter constraints you can create infinitely complex iterations of this idea. Whenever you have some group of entities that share common characteristics, define those commonalities in an interface that each will then implement. Then, you can add more generic methods to your repository to target scenarios that those common characteristics present.

Handling Entity-Specific Scenarios

What if there's something that only truly applies to one entity, though? What should you do in that circumstance. My personal recommendation here is to use partial classes and additional interfaces. Let's look at a simple example:
public class Cat : Entity<int>
{
    public bool IsLongHaired { get; set; }
}

public ICatRepository
{
    public IEnumerable<Cat> GetLongHairedCats();
}
Then, we just modify our IReadOnlyRepository interface and EntityFrameworkReadOnlyRepository class a bit:
public interface IReadOnlyRespository : ICatRepository
{
    ...
}

public partial class EntityFrameworkReadOnlyRepository<TContext> : IReadOnlyRepository
    where TContext : DbContext
{
    ...
}
Simply, we just added the ICatRepository as a base to our IReadOnlyRepository. In C# you can only inherit from one class, but you can implement any number of interfaces. Therefore, as the need arises you can continue to add additional interfaces in this way. Then, we added the partial keyword to our EntityFrameworkReadOnlyRepository class. This isn't strictly necessary. You always just add the GetLongHairedCats method directly here, but I prefer to segrate the generic code from the non-generic code. This keeps things more tidy and also lets you more easily get the code you need should do quite of bit of this type of customization. The partial keyword, if you aren't familiar, tells the compiler, simply, that the class is broken into multiple files, so it should collect all instances and combine them together. As far as your application is concerned, there's just one EntityFrameworkReadOnlyRepository and it has all the methods on it, but in your project it might be composed from many different locations.
Now, with that, lets add our implementation:
CatEntityFrameworkReadOnlyRepository.cs
public partial class EntityFrameworkReadOnlyRepository<TContext> : IReadOnlyRepository
    where TContext : DbContext
{
    public IEnumerable<Cat> GetLongHairedCats()
    {
        return GetQueryable<Cat>(m => m.IsLongHaired).ToList();
    }
}

Conclusion

I hope you can now see how powerful the combination of interfaces and generics can be. Where before you might have some monster Unit of Work class with a tens or hundreds of properties each holding a separate instance of a repository pertaining to a particular entity type, you have now just one relatively tidy and straight-forward class that can interact with any given entity class. Since it implements an interface, which acts a contract, you can create many different implementations and use dependency injection to inject just the right one for your application. Also, since it's truly generic, end-to-end, you can utilize this not just in one specific application but rather in many different applications. You could put all this in a class library and add that as a dependency to each project.
In the next couple of articles in this series, we'll look at a practical application of our generic repository and see how we can further use generics to make powerful framework for creating and administration interface for our entities.

Addendum: Caveat Emptor

I wanted to keep the code here as simple as possible, which is hard to do with complex ideas like this. However, I felt I would be remiss if I left you here without a little practical guidance concerning using partial classes and adding additional repository interfaces. You might have noticed that I violated the open-closed principle pretty blatantly in my discussing of this matter, and since I hinted at making a reusable class library, that's a pretty big deal. If I was actually doing this in my own application, I would create a separate interface/implementation just for my application. For example:
public interface IApplicationRepository : IRepository
{
}

public partial class ApplicationEntityFrameworkRepository
    : EntityFrameworkRepository<ApplicationDbContext>, IApplicationRepository
{
    public ApplicationEntityFrameworkRepository(ApplicationDbContext context)
        : base(context)
    {
    }
}
Now, with these, you can extend to your heart's content adding things not only specific to entities but to the particular application as well. You would just add an additional base interface to IApplicationRepository, as necessary and then create a new partial ApplicationEntityFrameworkRespository class to implement it. That way, you're not violating open-closed, and you keep your generic repository, well, generic, such that it can be reused without having the port around additional interfaces.

Comments

Popular posts from this blog

IGenricRepository

Summary of All Data Source Patterns