Where should I create the Unit of Work instance in an ASP.Net MVC 3 application?

泄露秘密 提交于 2019-12-02 23:32:23

I think you have a couple of changes to make:.

  1. Allow your DI container to inject a UnitOfWork instance into your Service classes in their constructors, and leave it out of your Controller altogether.

  2. 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...

  3. 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!