EF ObjectContext,Service and Repository - 管理上下文生命周期。

时间:2009-07-23 01:54:14

标签: c# entity-framework dependency-injection

我对MVP和实体框架世界相当新,所以请耐心等待。

我目前有一个View + Presenter组合,该视图有两个事件Edit和Delete,而演示者只是监听这些事件。我还有一个服务对象和存储库设置。服务层采用一些存储库实现,这些实现采用ObjectContext,因此构造的顺序是(将顶层对象传递给它下面的对象:

ObjectContext
   |
   V
Repositries
   |
   V
Service Object
   |
   V
Presenter

现在的问题是,当我在顶部创建对象上下文时,它一直存在,演示者处于活着状态,这意味着编辑和删除使用来自服务的相同上下文实例。

因此,调用ServiceObject.Delete和ServiceObject.Edit会使用相同的上下文,这使得很难管理更改跟踪。根据我的理解,上下文应该只是短暂的,只有一个工作单元,对我来说编辑和删除都是不同的工作。

如何使用实体框架进行DI并仍然管理上下文生命周期?

我看到人们刚刚在存储库中新建了对象上下文,这是一个很好的模式。

或者我应该在服务对象中执行此操作,例如:

ServiceObject{
  public void Edit(// some args) {
     Using(var context = new MyObjectContext) {
         var repo = new MyRepo(context);
         var entity = repo.GetForID(12);
         // Do some stuff for edit
         context.SaveChanges();
     }
  }
}

但是,如果我这样做,我不再将我的存储库传递给ServiceObject的构造函数而不执行DI :(。

在这种情况下我该怎么办?

有没有人知道我可以看到的任何开源项目可以帮助我解决这个问题。

感谢。

1 个答案:

答案 0 :(得分:24)

我将从顶部(演示者)开始,描述参与者之间的关系。

Presenter通过依赖项获取服务对象。使用合同概述服务功能:

class Presenter 
{
  public Presenter(IService service)
  {
     ... 
  }
}

从特定数据访问层实现中抽象出服务实现。基本上,每当服务执行一些需要数据源交互的操作时,它就会创建一个工作单元的实例,并在完成时将其处理掉。

interface IService
{
  void Do();
}

class Service : IService
{
  private readonly IUnitOfWorkFactory unitOfWorkFactory;
  public Service(IUnitOfWorkFactory unitOfWorkFactory)
  {
    this.unitOfWorkFactory = unitOfWorkFactory;
  }

  public void Do()
  {
    // Whenever we need to perform some data manipulation we create and later dispose
    // dispose unit of work abstraction. It is created through a factory to avoid 
    // dependency on particular implementation.
    using(IUnitOfWork unitOfWork = this.unitOfWorkFactory.Create())
    {
       // Unit of work holds Entity Framework ObjectContext and thus it used 
       // create repositories and propagate them this ObjectContext to work with
       IRepository repository = unitOfWork.Create<IRepository>();
       repository.DoSomethingInDataSource();

       // When we are done changes must be commited which basically means committing
       // changes of the underlying object context.
       unitOfWork.Commit();
    }
  }
}


/// <summary>
/// Represents factory of <see cref="IUnitOfWork"/> implementations.
/// </summary>
public interface IUnitOfWorkFactory
{
    /// <summary>
    /// Creates <see cref="IUnitOfWork"/> implementation instance.
    /// </summary>
    /// <returns>Created <see cref="IUnitOfWork"/> instance.</returns>
    IUnitOfWork Create();
}

/// <summary>
/// Maintains a list of objects affected by a business transaction and coordinates the writing out of 
/// changes and the resolution of concurrency problems.
/// </summary>
public interface IUnitOfWork : IDisposable
{
    /// <summary>
    /// Creates and initializes repository of the specified type.
    /// </summary>
    /// <typeparam name="TRepository">Type of repository to create.</typeparam>
    /// <returns>Created instance of the repository.</returns>
    /// <remarks>
    /// Created repositories must not be cached for future use because once this 
    /// <see cref="IUnitOfWork"/> is disposed they won't be able to work properly.
    /// </remarks>
    TRepository Create<TRepository>();

    /// <summary>
    /// Commits changes made to this <see cref="IUnitOfWork"/>.
    /// </summary>
    void Commit();
}

/// <summary>
/// Represents factory of <see cref="UnitOfWork"/>s. 
/// </summary>
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private readonly IUnityContainer container;

    /// <summary>
    /// Initializes a new instance of the <see cref="UnitOfWorkFactory"/> class.
    /// </summary>
    /// <param name="container">
    /// Dependency injection container instance used to manage creation of repositories 
    /// and entity translators.
    /// </param>
    public UnitOfWorkFactory(IUnityContainer container)
    {
                 this.conainer = container;
    }


    /// <summary>
    /// Creates <see cref="IUnitOfWork"/> implementation instance.
    /// </summary>
    /// <returns>Created <see cref="IUnitOfWork"/> instance.</returns>
    public IUnitOfWork Create()
    {
        var unitOfWork = this.container.Resolve<UnitOfWork>();
        unitOfWork.SetupObjectContext();
        return unitOfWork;
    }

     ... other members elidged for clarity
}

IUnitOfWork的实现接收IUnityContainer的实例,然后创建子容器并在那里注册ObjectContext实例。此子容器将用于创建存储库并传播ObjectContext。

以下是IUnitOfWork的简化实现:

class UnitOfWork : IUnitOfWork
{
  private readonly IUnityContainer container;
  private ObjectContext objectContext;

  public UnitOfWork (IUnityContainer container)
  {
    this.container = container.CreateChildContainer();
  }

  public void SetupObjectContext()
  {
    this.objectContext = ... // Create object context here
    this.container.RegisterInstance(context.GetType(), context);
  }

  public void Create<TRepository>()
  {
    // As long as we registered created object context instance in child container
    // it will be available now to repositories during resolve
    return this.container.Resolve<TRepository>();
  }

  public void Commit()
  {
     this.objectContext.SaveChanges();
  }
}

class Repository : IRepository
{
  private readonly SomeObjectContext objectContext;

  public Repository(SomeObjectContext objectContext)
  {
    this.objectContext = objectContext;
  }

  public void DoSomethingInDataSource()
  {
    // You can use object context instance here to do the work
  }
}