WPF / EntityFramework上下文生命周期

问题

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

技术规格

  • Wpf项目
  • .Net Framework 4客户端配置文件
  • MEF(包含在Framework 4.0 System.ComponentModel.Composition中)
  • 设计模式MVVM
  • 多用户应用程序

建筑

这是当前架构的架构。

架构架构

服务层

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

业务层

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

存储库层

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

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, IOrderReadOnlyRepository { [ImportingConstructor] public OrderReadOnlyRepository (IUnitOfWork uow) : base(uow) { } } 

ReadOnlyRepositoryBase

 public abstract class ReadOnlyRepositoryBase : IReadOnlyRepository where TEntity : class, IEntity where TContext : DbContext { protected readonly TContext _context; protected ReadOnlyRepositoryBase(IUnitOfWork uow) { _context = uow.Context; } protected DbSet DbSet { get { return _context.Set(); } public virtual IEnumerable GetAll(System.Linq.Expressions.Expression<Func> filter = null, Func<IQueryable, IOrderedQueryable> orderBy = null, string includeProperties = "") { IQueryable 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 All() { return DbSet.AsNoTracking(); } public virtual IQueryable AllWhere(Expression<Func> predicate) { return DbSet.Where(predicate).AsNoTracking(); } public virtual TEntity Get(Expression<Func> 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); } 

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

的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将使用默认构造函数实例化,该构造函数实例化上下文。

备注

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

目标实现

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

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

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

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

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

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

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

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

考虑通用算法:

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

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

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

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