WPF/EntityFramework上下文生命周期

toa*_*ast 5 c# architecture wpf entity-framework

问题

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

技术规格

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

建筑

这是当前架构的架构.

架构架构

服务层

  • 管理对业务规则的调用(业务层)
  • 业务规则完成后保存上下文(通过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;
   }
}
Run Code Online (Sandbox Code Playgroud)

服务层

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

   [ImportingConstructor]
   public OrderManagementService (IUnitOfWork unitOfWork, IOrderManagementBusiness orderManagementBusiness)
   {
      _unitOfWork= unitOfWork;
      _orderManagementBusiness = orderManagementBusiness;
   }
}
Run Code Online (Sandbox Code Playgroud)

业务层

public class OrderManagementBusiness : IOrderManagementBusiness
{
   private readonly IOrderReadOnlyRepository _orderReadOnlyRepository;

   [ImportingConstructor]
   public OrderManagementBusiness (IOrderReadOnlyRepository orderReadOnlyRepository)
   {
      _orderReadOnlyRepository = orderReadOnlyRepository;
   }
}
Run Code Online (Sandbox Code Playgroud)

ReadOnlyRepository图层

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

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);
   }
Run Code Online (Sandbox Code Playgroud)

我们可以看到上下文被赋予构造函数中的存储库.选择方法使用"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();
   }
}   
Run Code Online (Sandbox Code Playgroud)

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

备注

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

目标实现

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

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

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

Mar*_*age 3

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

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

  unitOfWork.Save();
}
Run Code Online (Sandbox Code Playgroud)

我已经修改了,UnitOfWork所以它实现了IDisposable。这将允许您处理 EF 上下文,并且还可以回滚事务(如果Save未调用)。如果您不需要额外的事务处理,您甚至可以摆脱该类UnitOfWork,因为它只是包装 EF 上下文,而您可以直接将 EF 上下文用作工作单元。

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