Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF6 (code first), MVC, Unity, and a service layer without a repository

My application is using SQL Server 2012, EF6, MVC and Web API.

It's also using a repository and assorted files such as:

DatabaseFactory.cs
Disposable.cs
IDatabaseFactory.cs
IRepository.cs
IUnitOfWork.cs
RepositoryBase.cs
UnitOfWork.cs

We already use a service layer between our controllers and the repository for some complicated business logic. We have no plans EVER to change to a different database and it has been pointed out to me that the recent thinking is that EF6 is a repository so why build another repository on top of it and why have all of the files I have above.

I am starting to think this is a sensible approach.

Does anyone know of any examples out there that implement EF6 without a repository, with a service layer. My search on the web has revealed many complex code examples that seem over complicated for no reason at all.

My problem is also when using a Service Layer then where do I put:

context = new EFDbContext()

In the controller, the service layer or both ? I read that I can do this with dependancy injection. I already use Unity as an IOC but I don't know how I can do this.

like image 349
Samantha J T Star Avatar asked Feb 17 '14 16:02

Samantha J T Star


2 Answers

Entity Framework IS already a Unit of Work pattern implementation as well as a generic repository implementation (DbContext is the UoW and DbSet is the Generic Repository). And I agree that it's way overkill in most apps to engineer another UoW or Generic Repository on top of them (besides, GenericRepsitory is considered to be an anti-pattern by some).

A Service layer can act as a concrete repository, which has a lot of benefits of encapsulating data logic that is specific to your business needs. If using this, then there is little need to build a repository on top of it (unless you want to be able to change your backend service technology, say from WCF to WebApi or whatever..)

I would put all your data access in your service layer. Don't do data access in your controller. That's leaking your data layer into your UI layer, and that's just poor design. It violates many of the core SOLID concepts.

But you do NOT need an additional UnitOfWork, or other layers beyond that in most cases, unless your apps are very complex and intended to work in multiple environments...

like image 153
Erik Funkenbusch Avatar answered Oct 14 '22 10:10

Erik Funkenbusch


Setting up Unity for ASP.NET MVC and WebAPI is quite easy if you install and add the Unity.Mvc* and Unity.WebAPI* Nuget packages to your project. (The * is a version number, like 3 or 4 or 5. Look for the appropriate versions for your project. Here are for example the links to the Unity.Mvc 5 package and to the Untity.WebAPI 5 package.)

The usage of these packages is explained in this blog post.

The building blocks are roughly like so:

You build a unity container and register all your dependencies there, especially the EF context:

private static IUnityContainer BuildContainer()
{
    var container = new UnityContainer();

    container.RegisterType<MyContext>(new HierarchicalLifetimeManager());

    container.RegisterType<IOrderService, OrderService>();
    container.RegisterType<ICustomerService, CustomerService>();
    container.RegisterType<IEmailMessenger, EmailMessenger>();
    // etc., etc.

    return container;
}

MyContext is your derived DbContext class. Registering the context with the HierarchicalLifetimeManager is very important because it will ensure that a new context per web request will be instantiated and disposed by the container at the end of each request.

If you don't have interfaces for your services but just concrete classes you can remove the lines above that register the interfaces. If a service needs to be injected into a controller Unity will just create an instance of your concrete service class.

Once you have built the container you can register it as dependency resolver for MVC and WebAPI in Application_Start in global.asax:

protected void Application_Start()
{
    var container = ...BuildContainer();

    // MVC
    DependencyResolver.SetResolver(
        new Unity.MvcX.UnityDependencyResolver(container));

    // WebAPI
    GlobalConfiguration.Configuration.DependencyResolver =
        new Unity.WebApiX.UnityDependencyResolver(container);
}

Once the DependencyResolvers are set the framework is able to instantiate controllers that take parameters in their constructor if the parameters can be resolved with the registered types. For example, you can create a CustomerController now that gets a CustomerService and an EmailMessenger injected:

public class CustomerController : Controller
{
    private readonly ICustomerService _customerService;
    private readonly IEmailMessenger _emailMessenger;

    public CustomerController(
        ICustomerService customerService,
        IEmailMessenger emailMessenger)
    {
        _customerService = customerService;
        _emailMessenger = emailMessenger;
    }

    // now you can interact with _customerService and _emailMessenger
    // in your controller actions
}

The same applies to derived ApiControllers for WebAPI.

The services can take a dependency on the context instance to interact with Entity Framework, like so:

public class CustomerService // : ICustomerService
{
    private readonly MyContext _myContext;

    public CustomerService(MyContext myContext)
    {
        _myContext = myContext;
    }

    // now you can interact with _myContext in your service methods
}

When the MVC/WebAPI framework instantiates a controller it will inject the registered service instances and resolve their own dependencies as well, i.e. inject the registered context into the service constructor. All services you will inject into the controllers will receive the same context instance during a single request.

With this setup you usually don't need a context = new MyContext() nor a context.Dispose() as the IOC container will manage the context lifetime.

like image 34
Slauma Avatar answered Oct 14 '22 09:10

Slauma