28

After a lots of reading and trying things out with Entity Framework latest stable version (6.1.1).

I'm reading lots of contradictions about whether or not to use repositories with EF6 or EF in general, because it's DbContext already provides a repository and DbSet the UoW, out of the box.

Let me first explain what my solution contains in terms of project and then I'll comeback at the contradiction.

It has a class library project, and an asp.net-mvc project. The class lib project being the data access and where Migrations are enabled for Code First.

In my class lib project I have a generic repository:

public interface IRepository<TEntity> where TEntity : class
{
    IEnumerable<TEntity> Get();

    TEntity GetByID(object id);

    void Insert(TEntity entity);

    void Delete(object id);

    void Update(TEntity entityToUpdate);
}

And here is the implementation of it:

public class Repository<TEntity> where TEntity : class
{
    internal ApplicationDbContext context;
    internal DbSet<TEntity> dbSet;

    public Repository(ApplicationDbContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual IEnumerable<TEntity> Get()
    {
        IQueryable<TEntity> query = dbSet;
        return query.ToList();
    }

    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

And here a few entities:

public DbSet<User> User{ get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<UserOrder> UserOrders { get; set; }
public DbSet<Shipment> Shipments { get; set; }

I don't what to repeat myself but, with EF6 you don't pass repositories anymore, but the DbContext instead. So for DI I've set up the following in the asp-net-mvc project using Ninject:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
}

And this will inject the ApplicationDbContext via constructor injection to upper layer classes where applicable.

Now coming back to the contradiction.

If we don't need a repository anymore because EF already provides that out of the box, how do we do Separation of Concern (abbreviated as SoC in title)?

Now correct me if I'm wrong, but it sounds to me like I just need to do all the data access logic/calculations (like adding, fetching, updating, deleting and some custom logic/calculations here and there (entity specific)) in the asp.net-mvc project, if I don't add a repository.

Any light on this matter is really appreciated.

Quoter
  • 4,236
  • 13
  • 47
  • 69
  • It's just a matter of how abstract you can get. EF context *is* indeed a repository, but a very opinionated repository with its own set of relational-database specific methods. If you want to abstract that away as well, you'll have to wrap EF with your own generic repository. – haim770 Dec 17 '14 at 14:31
  • The contradiction I read all over the internet is what bothered me the most. But now we got that cleared up, I'd like to know what the way to go is. Chris Pratt says with services. What do you say? – Quoter Dec 17 '14 at 19:54
  • Services have always been there (and popularized much due to the influence of Domain-Driven design) but I don't think they're meant to *replace* repositories. As far as I understand, they're merely a Façade for common actions and queries, and in most implementations you'll see an `IRepository` being inject to it. In the end, it all depends on the scope of your project. You need to ask yourself how important to the project is the level of abstraction you choose, and how exactly it will affect it in the long term, especially with regards to testability and scalability. – haim770 Dec 17 '14 at 20:57
  • You want to own your repository interface, and depend on that, rather than some implementation-specific type (like DbContext). – ssmith Nov 30 '16 at 13:47

1 Answers1

41

A little explanation will hopefully clear up your confusion. The repository pattern exists to abstract away database connection and querying logic. ORMs (object-relational mappers, like EF) have been around in one form or another so long that many people have forgotten or never had the immense joy and pleasure of dealing with spaghetti code littered with SQL queries and statements. Time was that if you wanted to query a database, you were actually responsible for crazy things like initiating a connection and actually constructing SQL statements from ether. The point of the repository pattern was to give you a single place to put all this nastiness, away from your beautiful pristine application code.

Fast forward to 2014, Entity Framework and other ORMs are your repository. All the SQL logic is packed neatly away from your prying eyes, and instead you have a nice programmatic API to use in your code. In one respect, that's enough abstraction. The only thing it doesn't cover is the dependency on the ORM itself. If you later decide you want to switch out Entity Framework for something like NHibernate or even a Web API, you've got to do surgery on your application to do so. As a result, adding another layer of abstraction is still a good idea, but just not a repository, or at least let's say a typical repository.

The repository you have is a typical repository. It merely creates proxies for the Entity Framework API methods. You call repo.Add and the repository calles context.Add. It's, frankly, ridiculous, and that's why many, including myself, say don't use repositories with Entity Framework.

So what should you do? Create services, or perhaps it's best said as "service-like classes". When services start being discussed in relation to .NET, all of sudden you're talking about all kinds of things that are completely irrelevant to what we're discussing here. A service-like class is like a service in that it has endpoints that return a particular set of data or perform a very specific function on some set of data. For example, whereas with a typical repository you would find your self doing things like:

articleRepo.Get().Where(m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now).OrderByDescending(o => o.PublishDate)

Your service class would work like:

service.GetPublishedArticles();

See, all the logic for what qualifies as a "published article" is neatly contain in the endpoint method. Also, with a repository, you're still exposing the underlying API. It's easier to switch out with something else because the base datastore is abstracted, but if the API for querying into that datastore changes you're still up a creek.

UPDATE

Set up would be very similar; the difference is mostly in how you use a service versus a repository. Namely, I wouldn't even make it entity dependent. In other words, you'd essentially have a service per context, not per entity.

As always, start with an interface:

public interface IService
{
    IEnumerable<Article> GetPublishedArticles();

    ...
}

Then, your implementation:

public class EntityFrameworkService<TContext> : IService
    where TContext : DbContext
{
    protected readonly TContext context;

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

    public IEnumerable<Article> GetPublishedArticles()
    {
        ...
    }
}

Then, things start to get a little hairy. In the example method, you could simply reference the DbSet directly, i.e. context.Articles, but that implies knowledge about the DbSet names in the context. It's better to use context.Set<TEntity>(), for more flexibility. Before I jump trains too much though, I want to point out why I named this EntityFrameworkService. In your code, you would only ever reference your IService interface. Then, via your dependency injection container, you can substitute EntityFrameworkService<YourContext> for that. This opens up the ability to create other service providers like maybe WebApiService, etc.

Now, I like to use a single protected method that returns a queryable that all my service methods can utilize. This gets rid of a lot of the cruft like having to initialize a DbSet instance each time via var dbSet = context.Set<YourEntity>();. That would look a little like:

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
{
    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;
}

Notice that this method is, first, protected. Subclasses can utilize it, but this should definitely not be part of the public API. The whole point of this exercise is to not expose queryables. Second, it's generic. In otherwords, it can handle any type you throw at it as long as there's something in the context for it.

Then, in our little example method, you'd end up doing something like:

public IEnumerable<Article> GetPublishedArticles()
{
    return GetQueryable<Article>(
        m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now,
        m => m.OrderByDescending(o => o.PublishDate)
    ).ToList();
}

Another neat trick to this approach is the ability to have generic service methods utilizing interfaces. Let's say I wanted to be able to have one method to get a published anything. I could have an interface like:

public interface IPublishable
{
    PublishStatus Status { get; set; }
    DateTime PublishDate { get; set; }
}

Then, any entities that are publishable would just implement this interface. With that in place, you can now do:

public IEnumerable<TEntity> GetPublished<TEntity>()
    where TEntity : IPublishable
{
    return GetQueryable<TEntity>(
        m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now,
        m => m.OrderByDescending(o => o.PublishDate)
    ).ToList();
}

And then in your application code:

service.GetPublished<Article>();
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Thank you very much for your detailed explanation. After reading it a few times, I still do have some questions. How would I create/use the `service` object? I'd like everything loosely coupled, no dependencies. An example code/project would be nice. Thought, I have been looking for one after reading your post, just couldn't find any that doesn't use repositories. – Quoter Dec 17 '14 at 19:24
  • Thanks, again, very nice and detailed explained. The way I used `DI` with `Ninject`, is that the way to go in your example as well? – Quoter Dec 17 '14 at 21:30
  • Yep. That'll cover the injection of the context. Then you just need `kernel.Bind().To>().InRequestScope()` to cover your controller dependency. – Chris Pratt Dec 17 '14 at 22:48
  • @ChrisPratt I am a little confused as this is my first time trying to implement this. Do you have any and all service methods within your EntityFrameworkService? I.E. If you were getting all active employees or maybe a list of office locations you would put them all in the same service? – Nicholas May 03 '16 at 19:56
  • Yes. The idea is you would have one class abstraction of your context. By using generic methods and interfaces, you can forgo a great many over-specialized methods. In situations where you do need a method that specific to a certain entity type, you can utilize the concept of partial classes to keep your code separated and orderly, while still utilizing just one class. – Chris Pratt May 04 '16 at 13:46
  • @ChrisPratt it would be great to see a sample project that uses this kind of setup. Much of the information out there still pushes repository/UOW patterns and the few cases I have found that don't are so complex they are hard to wrap your head around. Any chance you will ever make one or do you have one your recommend? – Nicholas May 04 '16 at 14:36
  • I actually just polished off the first set of articles about just this topic on my blog: http://cpratt.co/truly-generic-repository/ – Chris Pratt May 05 '16 at 15:00
  • @ChrisPratt As always you are awesome! I have come across your articles before and they have always been helpful. – Nicholas May 06 '16 at 19:24
  • Cool stuff @ChrisPratt just got an error with GetPublished
    (); solution found under: http://stackoverflow.com/a/6451258/767204 e.g. where TEntity : class, IPublishable
    – Red Aug 23 '16 at 05:05
  • Note the type of Expression> for filter (not Func). This causes the evaluation to be done in Sql rather than in memory. See https://stackoverflow.com/questions/37348488/ef-sql-changed-when-passing-predicate-as-parameter-to-where-clause – Hans Vonn Oct 13 '17 at 15:48