WPF / EntityFramework上下文生命周期 [英] WPF / EntityFramework Context Lifetime

查看:154
本文介绍了WPF / EntityFramework上下文生命周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are currently having a problem of architecture on a WPF application. It concerns EntityFramework context management, it’s instantiated once and used during the entire life of the application. So we end up with a cache issue, entities are not updated when they were loaded once. Our entities are obsolete when using the application.


  • Wpf项目

  • .Net Framework 4客户端配置文件

  • MEF(包含在Framework 4.0 System.ComponentModel.Composition中)

  • 设计模式MVVM

  • 多用户应用程序

  • Wpf project
  • .Net Framework 4 client Profile
  • MEF (Include in Framework 4.0 System.ComponentModel.Composition)
  • Design pattern MVVM
  • Multi users application

当前架构的架构。


  • 管理业务规则(业务层) li>
  • 在业务规则完成后保存上下文(通过UnitOfWork)

  • 只能由ViewModel调用


  • 定义业务规则

  • 可以只能由服务层调用


    执行更改上下文数据的方法(insert,u pdate,delete)
  • 继承ReadOnlyRepository

  • 只能由业务层调用


  • 返回数据的执行方法(select)

  • 可以调用无处不在(ViewModel,服务层,业务层)


  • 管理上下文实例

  • 保存上下文

  • 上下文仅适用于存储库

[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;
   }
}



服务层



Service layer

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

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



业务层



Business layer

public class OrderManagementBusiness : IOrderManagementBusiness
{
   private readonly IOrderReadOnlyRepository _orderReadOnlyRepository;

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



ReadOnlyRepository层



ReadOnlyRepository layer

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



ReadOnlyRepositoryBase



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);
   }

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

We can see that the context is given to the repository in the constructor. Select methods use the "AsNoTracking ()" method to not cache entities. It's a temporary solution which is obviously not viable in long term.

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

During the first composition of a service with MEF, UnitOfWork will be instantiated with the default constructor which instantiate the context.

某些代码段已被省略

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

The lifetime of the context is clearly an issue. Knowing that all calls within the same service method must share the same context.

我们如何考虑修改架构以避免单个上下文?

How can we consider modifying the architecture to avoid having a single context ?

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

Feel free to ask questions ! If needed, I can attach a test project which highlight the issue.

推荐答案

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

In your application there is only single unit of work but that is not he purpose of a unit a work. Instead, you need to create a unit of work each time "you work with the database". In your case the UnitOfWork should not be part of the MEF container but you can create a UnitOfWorkFactory and inject it from the container. Then the services can create a UnitOfWork each time "work has to be done" with the database:

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

  unitOfWork.Save();
}

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

I have modified UnitOfWork so it implements IDisposable. This will allow you to dispose the EF context and also perhaps rollback a transaction if Save was not called. If you have no need for the extra transaction handling you can even get rid of the UnitOfWork class because it simply wraps the EF context and instead you can used the EF context as a unit of work directly.

此更改将改变强制您修改服务和存储库的结构,但您真的必须,因为您的问题是您有一个单位在整个应用程序中存在的工作。

This change will change force you to modify how the service and the repositories are structured but you really have to because your issue is that you have a single unit of work that exists for the entire duration of the application.

这篇关于WPF / EntityFramework上下文生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆