I have read as many of the posts on Stackoverflow as I can find with regards the use of a Unit of Work pattern within an ASP.Net MVC 3 application which includes a Business Layer. However, I still have a couple of questions with regards this topic and would greatly appreciate any feedback people can give me.
I am developing an ASP.Net MVC 3 Web application which uses EF 4.1. I will be using both the Repository and Unit of Work Patterns with this project similar to how they are used in this great tutorial
The difference in my project is that I need to also include a Business Layer (separate project in my solution) in order to carry out the various business rules for the application. The tutorial mentioned above does not have a Business layer, and therefore creates an instance of the Unit of Work class from the controller
public class CourseController : Controller
{
private UnitOfWork unitOfWork = new UnitOfWork();
However, my question is, where should I create the instance of the Unit of Work class if I have a Business Layer?
I personally think it should be created in my controller and then injected into the Business Layer like so:
public class PeopleController : Controller
{
private readonly IUnitOfWork _UoW;
private IPersonService _personService;
public PeopleController()
{
_UoW = new UnitOfWork();
_personService = new PersonService(_UoW);
}
public PeopleController(IUnitOfWork UoW, IPersonService personService)
{
_UoW = UoW;
_personService = personService;
}
public ActionResult Edit(int id)
{
Person person = _personService.Edit(id);
return View(person);
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
private BlogEntities _context = new BlogEntities();
private PersonRepository personRepository = null;
public IPersonRepository PersonRepository
{
get
{
if (this.personRepository == null)
{
this.personRepository = new PersonRepository(_context);
}
return personRepository;
}
}
public void Save()
{
_context.SaveChanges();
}
public class PersonService : IPersonService
{
private readonly IUnitOfWork _UoW;
public PersonService(IUnitOfWork UoW)
{
_UoW = UoW;
}
public Person Edit(int id)
{
Person person = _UoW.PersonRepository.GetPersonByID(id);
return person;
}
public class PersonRepository : IPersonRepository
{
private readonly BlogEntities _context;
public PersonRepository(BlogEntities context)
{
_context = context;
}
public Person GetPersonByID(int ID)
{
return _context.People.Where(p => p.ID == ID).Single();
}
I have read others saying that the Unit of Work instantiation should not be in the Controller, but created in the Service Layer instead. The reason why I am not so sure about this approach is because my Controller may have to use several different Service Layers in one business transaction, and if the Unit of Work instance was created inside each Service, it would result in several Unit of Work instances being created, which defeats the purpose, ie, one Unit of Work per business transaction.
Maybe what I have explained above is wrong, but if so, I would greatly appreciate if someone could put me right.
Thanks again for your help.
The Model-View-Controller (MVC) architectural pattern separates an application into three main components: the model, the view, and the controller.
Unit of Work in C# Repository Pattern The Unit of Work pattern is used to group one or more operations (usually database CRUD operations) into a single transaction or “unit of work” so that all operations either pass or fail as one unit.
The unit of work class serves one purpose: to make sure that when you use multiple repositories, they share a single database context. That way, when a unit of work is complete you can call the SaveChanges method on that instance of the context and be assured that all related changes will be coordinated.
I think you have a couple of changes to make:.
Allow your DI container to inject a UnitOfWork
instance into your Service classes in their constructors, and leave it out of your Controller altogether.
If your DI container supports it (Ninject does, for example), configure your UnitOfWork
to be managed on a per-request basis; this way your services will be handed a distinct UnitOfWork
for each request, and you're all done. Or...
If your DI container does not support per-request lifetimes, configure it to manage the UnitOfWork
as a singleton, so every Service
class gets the same instance. Then update your UnitOfWork
to store its Entities
object in a data store which stores objects on a per-request basis, for example in HttpContext.Current.Items
, as described here.
Edit 1
Regarding where the UnitOfWork
should be injected; I'd say the Service layer is the correct place. If you imagine your system as a series of layers where the outer layers deal with user interactions and the lower layers deal with data storage, each layer should become less concerned with users and more concerned with data storage. UnitOfWork
is a concept from one of the 'lower-level' layers and Controller is from a higher-level layer; your Service
layer fits between them. It therefore makes sense to put the UnitOfWork
into the Service
class rather than the Controller
.
Edit 2
To elaborate on the UnitOfWork
creation and it's relationship to HttpContext.Current.Items
:
Your UnitOfWork
would no longer hold a reference to an Entities
object, that would be done through the HttpContext
object, injected into the UnitOfWork
behind an interface like this:
public interface IPerRequestDataStore : IDisposable
{
bool Contains(string key);
void Store<T>(string key, T value);
T Get<T>(string key);
}
The HttpContext
object would then implement IPerRequestDataStore
like this:
public class StaticHttpContextPerRequestDataStore : IPerRequestDataStore
{
public bool Contains(string key)
{
return HttpContext.Current.Items.Contains(key);
}
public void Store<T>(string key, T value)
{
HttpContext.Current.Items[key] = value;
}
public T Get<T>(string key)
{
if (!this.Contains(key))
{
return default(T);
}
return (T)HttpContext.Current.Items[key];
}
public void Dispose()
{
var disposables = HttpContext.Current.Items.Values.OfType<IDisposable>();
foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
}
As an aside, I've called it StaticHttpContextPerRequestDataStore
as it uses the static HttpContext.Current
property; that's not ideal for unit testing (another topic altogether), but at least the name indicates the nature of its dependency.
Your UnitOfWork
then passes the IPerRequestDataStore
it's given to each of its Repository
objects so they can access the Entities
; this means that no matter how many UnitOfWork
instances you create, you'll use the same Entities
object throughout a request because it's stored and retrieved in the IPerRequestDataStore
.
You'd have an abstract base Repository
which would use its IPerRequestDataStore
to lazy-load its Entities
object like this:
public abstract class RepositoryBase : IDisposable
{
private readonly IPerRequestDataStore _dataStore;
private PersonRepository personRepository;
protected RepositoryBase(IPerRequestDataStore dataStore)
{
this._dataStore = dataStore;
}
protected BlogEntities Context
{
get
{
const string contextKey = "context";
if (!this._dataStore.Contains(contextKey))
{
this._dataStore.Store(contextKey, new BlogEntities());
}
return this._dataStore.Get<BlogEntities>(contextKey);
}
}
public void Dispose()
{
this._dataStore.Dispose();
}
}
Your PeopleRepository
(for example) would look like this:
public class PeopleRepository : RepositoryBase, IPersonRepository
{
public PeopleRepository(IPerRequestDataStore dataStore)
: base(dataStore)
{
}
public Person FindById(int personId)
{
return this.Context.Persons.FirstOrDefault(p => p.PersonId == personId);
}
}
And finally, here's the creation of your PeopleController
:
IPerRequestDataStore dataStore = new StaticHttpContextDataStore();
UnitOfWork unitOfWork = new UnitOfWork(dataStore);
PeopleService service = new PeopleService(unitOfWork);
PeopleController controller = new PeopleController(service);
One of the central concepts here is that objects have their dependencies injected into them via their constructors; this is generally accepted as good practice, and more easily allows you to compose objects from other objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With