Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF6 Code First with generic repository and Dependency Injection and SoC

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.

like image 342
Quoter Avatar asked Dec 17 '14 14:12

Quoter


People also ask

Which contains generic methods for frequently used CRUD operations?

A generic repository is a type that comprises of a set of generic methods for performing CRUD operations. However, it's just another anti pattern and is used frequently with Entity Framework to abstract calls to the data access layer.

What is generic repository pattern C#?

The generic repository pattern implements in a separate class library project. It uses the "Code First" development approach and creates a database from a model, using migration. This article demonstrates a sample Application, which has one too many relationships in ASP.NET Core with Entity Framework Core.

How do you inject Dbcontext into repository?

You can define a RepositoryConnection class in App. Data that acts as a wrapper to the Context and removes the need to reference EF in App. Web . If you are using an IoC Container you can control the lifetime of the RepositoryConnection class to ensure that all instances of Repository get the same Context.


1 Answers

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>(); 
like image 128
Chris Pratt Avatar answered Sep 19 '22 20:09

Chris Pratt