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中打开一个项目;开始生命周期范围来管理所有资源,这些资源应该在项目’生命’时可用)。