关于在其中使用工作单元模式的问题,我已经阅读了许多关于Stackoverflow的帖子。
一个包含业务层的ASP.Net MVC 3应用程序。但是,我还有几个问题
关于这个话题,非常感谢人们给我的任何反馈。

我正在开发使用EF 4.1的ASP.Net MVC 3 Web应用程序。我将同时使用存储库和
此项目的工作单元模式类似于this优秀教程中的使用方式

我的项目的不同之处在于,我还需要包括一个业务层(解决方案中的单独项目),以便
执行该应用程序的各种业务规则。上面提到的教程没有业务层,并且
因此,从控制器创建工作单元类的实例

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中,而应在Service Layer中创建
代替。我对这种方法不太确定的原因是,我的控制器可能必须使用几种不同的方法
一项业务交易中的服务层,如果在每个服务中都创建了工作单元实例,则会导致多个
正在创建工作单元实例,这违反了目的,即,每个业务交易一个工作单元。

也许上面我解释的是错误的,但是如果是这样,那么如果有人可以说对我,我将不胜感激。

再次感谢您的帮助。

最佳答案

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


允许您的DI容器将UnitOfWork实例注入其构造函数中的Service类,并将其完全排除在Controller外。
如果您的DI容器支持它(例如Ninject支持),则将UnitOfWork配置为按请求进行管理;这样,将为每个请求将您的服务分配给不同的UnitOfWork,您已经完成了。要么...
如果您的DI容器不支持每个请求的生存期,请将其配置为将UnitOfWork作为单个实例进行管理,以便每个Service类都具有相同的实例。然后更新您的UnitOfWork以将其Entities对象存储在一个数据存储中,该数据存储按请求存储对象,例如,在HttpContext.Current.Items中,如here所述。


编辑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);


这里的中心概念之一是对象通过其构造函数注入了它们的依赖关系。这通常被认为是一种很好的做法,并且可以更轻松地使您由其他对象组成对象。

10-07 21:39