7
votes

I see several questions relating somewhat to this, but I still can't find the answer I'm looking for, so I'm posting my question. If another question holds the answer (and I'm just not seeing it), please point me to it.

I'm trying to figure out where my UnitOfWork belongs -- and specifically, gets created -- when using EF4 and Unity with the Repository pattern.

Basically, I have a service that is used to implement my business logic. This service constructor takes in the repository, so the service gets injected with my repository. The service then uses the injected repository to carry out actions against the data store -- but I need to wrap these in a unit of work.

My unit of work, however, needs to be injected with the EF4 context (or, in my case, and interface of the context -- IObjectContext). And I'm not sure where the UoW should be created and injected w/ the context.

Here are the possible options I can think of, none of which seem ideal:

  • Include the UoW in the service constructor, thus having the service injected w/ the unit of work, which in turn is injected w/ my EF4 context. But this seems wrong because I don't want my UoW created on every instance of the repository.

  • Do an on-demand creation using container.Resolve to get an instance of the UoW, injecting my EF4 context. This seems excessive having to constantly hit the IoC container, rather than already having access to the UoW.

  • Inject the context directly into the service, allowing me to create a UoW(context). This seems bad since I've now exposed the context to the service, and this should be isolated to the repository.

So my question is, is one of these methods acceptable, or is there another method I'm not thinking of?

Thanks in advance.

2

2 Answers

12
votes

There are probably several ways how to use this so I will describe one which I found useful.

Imho the place to define UoW is in Application logic - the logic which calls your business layer (business services). The reason for this is that UoW should represent logical business trasaction - application logic (or service facade in case of remote calls) defines what is logical transaction. So for example in MVC you can go with architecture where each controller action represents single UoW:

public class MyController : Controller
{
  public MyController(IFirstService firstService, ISecondService secondService,
    IUnitOfWork unitOfWork)
  { ... }

  [HttpPost]
  public ActionResult SomeAction(Model data)
  {
    _firstService.SomeProcessing(data);
    _secondService.SomeProcessing(data);
    _unitOfWork.SaveChanges();
    return RedirectToAction(...);
  }
}

In this example my controller is depenent on two business services and action calls them both - UoW then save changes performed by both services. That is the reason why I think the UoW should be available in controller because if your application layer don't have access to UoW you can't compose (reuse) your logic from several service calls (because each probably calls its own SaveChanges).

Other approach is with service facade. Facade will be public interface of your business layer and it will hide service composition:

_firstService.SomeProcessing(data);
_secondService.SomeProcessing(data);
_unitOfWork.SaveChanges();

In such case UoW will not be passed to controller but to service facade and service facade will be injected to controller. You will definitely use this approach if your business logic will be exposed over web service (or other remote technology).

The last problem which you have to deal with is passing UoW to services. Services as well as UoW are injected into controller (presenter, service facade or whatever) but in the same time UoW (or ObjectContext) must be injected into services so that internally used repositories can work with it. For this you need correct IoC lifetime manager so that it returns same instance for all injections within same "request". In case of web application you need PerHttpRequest lifetime manager (which you must implement by yourselves because Unity does not provide it).

1
votes

One way is to manage this is to use the method described in http://mfelicio.wordpress.com/2010/02/07/managing-the-entity-framework-objectcontext-instance-lifetime-in-wcf-and-sharing-it-among-repositories/ That article implements the ContextManager for Wcf services. For ASP.NET app we could use something like this.

public class AspNetDBContextManager<TContext> : IDBContextManager
    where TContext : IDBContext, new()
{
    #region IDBContextManager Members

    public IDBContext GetDBContext()
    {
        return this.GetOrCreateDbContext();
    }

    private IDBContext GetOrCreateDbContext()
    {
        if (HttpContext.Current == null)
        {
            throw new InvalidOperationException("Can be used only within ASP.NET applications");
        }

        string dbContextKey = string.Format("__AspNetDBCM__{0}__", HttpContext.Current.GetHashCode());

        object dbContext = HttpContext.Current.Items[dbContextKey];

        if (dbContext == null)
        {
            dbContext = new TContext();

            if (dbContext != null)
            {
                HttpContext.Current.Items[dbContextKey] = dbContext;
            }
        }

        return dbContext as IDBContext;
    }

    #endregion
}

public interface IDBContext
{
    object Context { get; }
}


public interface IDBContextManager
{
    IDBContext GetDBContext();
}