WPF / EntityFramework上下文生命周期

时间:2014-10-03 15:04:34

标签: c# wpf entity-framework architecture

问题

我们目前在WPF应用程序上遇到架构问题。它涉及EntityFramework上下文管理,它实例化一次并在应用程序的整个生命周期中使用。因此,我们最终会遇到缓存问题,实体在加载一次时不会更新。使用该应用程序时,我们的实体已过时。

技术规范

  • Wpf项目
  • .Net Framework 4客户端个人资料
  • MEF(包含在Framework 4.0 System.ComponentModel.Composition中)
  • 设计模式MVVM
  • 多用户应用

建筑

这是当前架构的架构。

architecture schema

服务层

  • 管理对业务规则(业务层)的调用
  • 完成业务规则后保存上下文(通过UnitOfWork)
  • 只能由ViewModel
  • 调用

业务层

  • 定义业务规则
  • 只能由服务层调用

存储库层

  • 执行更改上下文数据的方法(插入,更新,删除)
  • 继承ReadOnlyRepository
  • 只能由业务层调用

ReadOnlyRepository图层

  • 执行返回数据的方法(选择)
  • 可以随处调用(ViewModel,服务层,业务层)

的UnitOfWork

  • 管理上下文实例
  • 保存上下文
  • 仅适用于存储库的上下文

代码

视图模型

[Export(typeof(OrderViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderViewModel : ViewModelBase
{
   private readonly IOrderManagementService _orderManagementService;
   private readonly IOrderReadOnlyRepository _orderReadOnlyRepository;

   [ImportingConstructor]
   public OrderViewModel(IOrderManagementService orderManagementService, IOrderReadOnlyRepository orderReadOnlyRepository)
   {
      _orderManagementService = orderManagementService;
      _orderReadOnlyRepository = orderReadOnlyRepository;
   }
}

服务层

public class OrderManagementService : IOrderManagementService
{
   private readonly IUnitOfWork _unitOfWork;
   private readonly IOrderManagementBusiness _orderManagementBusiness;

   [ImportingConstructor]
   public OrderManagementService (IUnitOfWork unitOfWork, IOrderManagementBusiness orderManagementBusiness)
   {
      _unitOfWork= unitOfWork;
      _orderManagementBusiness = orderManagementBusiness;
   }
}

业务层

public class OrderManagementBusiness : IOrderManagementBusiness
{
   private readonly IOrderReadOnlyRepository _orderReadOnlyRepository;

   [ImportingConstructor]
   public OrderManagementBusiness (IOrderReadOnlyRepository orderReadOnlyRepository)
   {
      _orderReadOnlyRepository = orderReadOnlyRepository;
   }
}

ReadOnlyRepository图层

public class OrderReadOnlyRepository : ReadOnlyRepositoryBase<DataModelContainer, Order>, IOrderReadOnlyRepository
{
   [ImportingConstructor]
   public OrderReadOnlyRepository (IUnitOfWork uow) : base(uow)
   {
   }
}

ReadOnlyRepositoryBase

public abstract class ReadOnlyRepositoryBase<TContext, TEntity> : IReadOnlyRepository<TEntity>
   where TEntity : class, IEntity
   where TContext : DbContext
{
   protected readonly TContext _context;

   protected ReadOnlyRepositoryBase(IUnitOfWork uow)
   {
      _context = uow.Context;
   }

   protected DbSet<TEntity> DbSet
   {
      get { return _context.Set<TEntity>();
   }

   public virtual IEnumerable<TEntity> GetAll(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
   {
        IQueryable<TEntity> query = DbSet.AsNoTracking();

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        return query.ToList();
   }

   public virtual IQueryable<TEntity> All()
   {
      return DbSet.AsNoTracking();
   }

   public virtual IQueryable<TEntity> AllWhere(Expression<Func<TEntity, bool>> predicate)
   {
      return DbSet.Where(predicate).AsNoTracking();
   }

   public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
   {
      return DbSet.Where(predicate).AsNoTracking().FirstOrDefault();
   }

   public virtual TEntity GetById(int id)
   {
      TEntity find = DbSet.Find(id);
      _context.Entry(find).State = System.Data.EntityState.Detached;
      return DbSet.Find(id);
   }

我们可以看到上下文被赋予构造函数中的存储库。选择方法使用&#34; AsNoTracking()&#34;不缓存实体的方法。这是一个暂时的解决方案,显然在长期内不可行。

的UnitOfWork

public class UnitOfWork : IUnitOfWork
{
   private DataModelContainer _context;

   public UnitOfWork()
      : this(new DataModelContainer())
   {
   }

   public UnitOfWork(DataModelContainer context)
   {
      _context = context;
   }

   public DataModelContainer Context
   {
      get { return _context; }
   }

   public int Save()
   {
      return _context.SaveChanges();
   }
}   

在使用MEF的第一个服务组合期间,UnitOfWork将使用默认构造函数实例化,该构造函数实例化上下文。

说明

为了便于阅读,省略了一些代码。

实现目标

背景的生命周期显然是一个问题。知道同一服务方法中的所有调用必须共享相同的上下文。

我们如何考虑修改体系结构以避免出现单个上下文?

随意提问!如果需要,我可以附上一个突出问题的测试项目。

2 个答案:

答案 0 :(得分:2)

在你的申请中,只有一个单位的工作,但这不是一个单位工作的目的。相反,每次“使用数据库”时都需要创建一个工作单元。在您的情况下,UnitOfWork不应该是MEF容器的一部分,但您可以创建UnitOfWorkFactory并从容器中注入它。然后,每次“必须完成工作”时,服务可以创建UnitOfWork

using (var unitOfWork = unitOfWorkFactory.Create()) {
  // Do work ...

  unitOfWork.Save();
}

我修改了UnitOfWork,因此它实现了IDisposable。这将允许您处置EF上下文,如果未调用Save,也可能回滚事务。如果您不需要额外的事务处理,您甚至可以删除UnitOfWork类,因为它只包含EF上下文,而是可以直接使用EF上下文作为工作单元。

此更改将强制您修改服务和存储库的结构,但您确实必须这样做,因为您的问题是您在整个应用程序期间只有一个工作单元。

答案 1 :(得分:1)

概述明确区分的用例,这将保持自己的生命周期范围。这有助于防止其他资源泄漏(使用WPF时非常频繁)。

考虑通用算法:

  • 初始化生命周期范围。
  • 使用范围:
    • 分配视图和其他WPF资源,分配业务层,数据访问(UoW,上下文,回购)。
    • 从数据库加载数据并将其显示给用户。
    • 等待用户操作(1)。
    • 进行一些更改或从DB加载更多数据。
    • 更新用户的数据表示。
    • 转到(1)直到方案完成。
  • 处理范围,取消分配资源。

问题是您的范围目前是您的应用程序

现在假设您在视图级别管理范围。您可以分配,显示视图,获取用户输入,保存更改,然后立即处理整个对象树。

显然,你应该灵活地使用范围。有时在视图级别使用它(比如“编辑项目”)会很有用,有时它可能会分散在多个视图中(例如向导)。您甚至可以维护数据驱动的范围(假设您在Visual Studio中打开一个项目;开始生命周期范围来管理所有资源,这些资源应该在项目'生命'时可用)。