我应该在ASP.Net MVC 3应用程序中的哪个位置创建工作单元实例?

时间:2012-02-02 15:53:06

标签: asp.net-mvc-3 architecture repository-pattern unit-of-work

我已经阅读了Stackoverflow上的很多帖子,因为我可以找到关于使用工作单元模式的内容 ASP.Net MVC 3应用程序,包括业务层。但是,我还有几个问题 关注这个话题,非常感谢人们给我的任何反馈。

我正在开发一个使用EF 4.1的ASP.Net MVC 3 Web应用程序。我将使用存储库和 该项目的工作单元模式类似于this精彩教程

中的使用方式

我的项目的不同之处在于我还需要包含一个业务层(我的解决方案中的单独项目) 执行应用程序的各种业务规则。上面提到的教程没有Business层,并且 因此,从控制器

创建一个Unit of Work类的实例
public class CourseController : Controller
{
    private UnitOfWork unitOfWork = new UnitOfWork();

但是,我的问题是,如果我有业务层,我应该在哪里创建工作单元类的实例?

我个人认为它应该在我的控制器中创建,然后像这样注入业务层:

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();
    }

我读过其他人说过,工作单元实例化不应该在Controller中,而应该在服务层中创建 代替。我不太确定这种方法的原因是因为我的控制器可能必须使用几种不同的方法 服务层在一个业务事务中,如果在每个服务中创建了工作单元实例,则会产生多个 正在创建的工作单元实例,这会破坏目的,即每个业务事务的一个工作单元。

也许我上面解释的是错误的,但如果是这样的话,如果有人能说得对,我会非常感激。

再次感谢您的帮助。

1 个答案:

答案 0 :(得分:18)

我认为您需要进行一些更改:。

  1. 允许您的DI容器将UnitOfWork实例注入其构造函数中的Service类,并将其完全从Controller中删除。

  2. 如果您的DI容器支持它(例如Ninject),请根据请求配置您的UnitOfWork进行管理;通过这种方式,您的服务将为每个请求提供一个独特的UnitOfWork,并且您已完成所有操作。或...

  3. 如果您的DI容器不支持每个请求的生命周期,请将其配置为将UnitOfWork作为单例进行管理,这样每个Service类都会获得相同的实例。然后更新您的UnitOfWork以将其Entities对象存储在数据存储中,该数据存储基于每个请求存储对象,例如在HttpContext.Current.Items中,如here所述。

  4. 修改1

    关于应该注射UnitOfWork的地方;我说服务层是正确的地方。如果您将系统想象为一系列层,其中外层处理用户交互,而较低层处理数据存储,则每层应该更少关注用户并更关注数据存储。 UnitOfWork是来自“低层”层之一的概念,而Controller来自更高层层;您的Service图层适合他们。因此,将UnitOfWork放入Service类而不是Controller是有意义的。

    修改2

    详细说明UnitOfWork创建及其与HttpContext.Current.Items的关系:

    您的UnitOfWork将不再保留对Entities对象的引用,这将通过HttpContext对象完成,注入到此类界面后面的UnitOfWork

    public interface IPerRequestDataStore : IDisposable
    {
        bool Contains(string key);
    
        void Store<T>(string key, T value);
    
        T Get<T>(string key);
    }
    

    然后HttpContext对象将实现IPerRequestDataStore,如下所示:

    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();
            }
        }
    }
    

    顺便说一句,我称之为StaticHttpContextPerRequestDataStore,因为它使用静态HttpContext.Current属性;这不是单元测试的理想选择(完全是另一个主题),但至少名称表明了它的依赖性。

    然后,您的UnitOfWork会将IPerRequestDataStore传递给每个Repository个对象,以便他们可以访问Entities;这意味着无论您创建了多少UnitOfWork个实例,您都会在整个请求中使用相同的Entities对象,因为它在IPerRequestDataStore中存储和检索。

    您有一个抽象基础Repository,可以使用其IPerRequestDataStore延迟加载其Entities对象,如下所示:

    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();
        }
    }
    

    您的PeopleRepository(例如)看起来像这样:

    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);
        }
    }
    

    最后,这是PeopleController

    的创建
    IPerRequestDataStore dataStore = new StaticHttpContextDataStore();
    UnitOfWork unitOfWork = new UnitOfWork(dataStore);
    PeopleService service = new PeopleService(unitOfWork);
    PeopleController controller = new PeopleController(service);
    

    这里的一个核心概念是对象通过其构造函数将依赖项注入其中;这通常被认为是一种良好的做法,更容易让你从其他对象中组合对象。