我在桌面应用程序中使用EF6 + WPF和MVVM设计模式。我也使用Autofac作为DI容器。
我阅读了很多关于EF上下文生命周期管理的内容,并且我决定只为单一视图模型实例提供一个EF上下文实例。我发现了一些关于这种方法的有趣文章,所以我认为这只是管理EF上下文的好方法。我使用Autofac来管理EF生命周期,因此每次创建新的视图模型时,都只会创建一个新的EF上下文。
但当然,我遇到了一个问题。我的大多数EF查询都运行良好,但以下查询始终返回旧(缓存)值。每次按“执行”按钮时都会调用此查询,因此每个视图/视图模型执行的次数很多
this.context.someTable.Where(arg => arg.value == "value").Single();
我知道我总是可以使用以下代码重新加载实体
this.context.Entry(entity).Reload();
但对我来说这不是一个好的解决方案。我也知道,如果我处理当前上下文并在下一个查询之前重新创建,我将始终接收当前值。但是这种方法与每种视图模型方法的一个上下文冲突。
我应该修改/更改什么以避免EF缓存问题并且仍然具有良好的性能。
答案 0 :(得分:8)
您不应该坚持上下文
我建议你放弃单一的共享上下文。我最近为大型WPF应用程序做了这个。 EF上下文被设计为一个工作单元,您应该使用它,然后调用.Dispose()
。如果您需要热切地阅读关系属性,则应使用.Include()
提示。您应该在using
块中构造上下文,以便知道丢失范围的位置,并确保处理上下文。
你会发现EF的性能实际上可以减少,因为它需要引用它的内部缓存和状态。我发现如果使用共享上下文,批量数据插入模式会恶化。 EF的性能不如RDBMS。
正如您所经历的那样,您可以保留上下文并从缓存实体中受益,但如果由于系统的性质和用户的要求而变得很痛苦,那么您不再真正受益于缓存。您的支持RDBMS应该足够快。只要以任何方式缓存(包括EF二级缓存和ASP.NET输出缓存),您就需要立即计划如何使缓存的实体过期。这为您的编码人员增加了更多工作,并为您的系统提供了一种全新的失败方式。
例如,考虑EF的一个好处是自动解决关系属性。您可以无缝地跨越数据图表,直到您遇到缓存和陈旧的实体。在这种情况下,在检索此类实体之前很难使缓存失效。
但是如果您必须重新加载更新
如果您确实不想将您的体系结构更改为Microsoft建议/预期的方式。我建议你跟踪所有打开的上下文(在构造时添加静态集合,在处理时删除,使用终结器模式进行双重检查,以及在dispose上进行终结器抑制),并在save管道中实现一些通用代码(有几种方法)执行此操作)尝试在所有打开的上下文中重新加载实体。这是使EF实体缓存过期的主动方式。这可能会影响较大集合的性能,但您可以使用白名单或黑名单来处理,而不是处理所有已保存的实体。
就个人而言,我很高兴我做出了改变(重组为短命境),从长远来看,在代码可维护性和系统稳定性方面有很大的好处。
答案 1 :(得分:3)
以下方法强制EF重新查询数据库的查询,不要缓存结果:
this.context.someTable.AsNoTracking().Where(arg => arg.value == "value").Single();
重要的方法是AsNoTracking
答案 2 :(得分:0)
如果您使用MVVM,那么您可以这样做:您的视图绑定到您的视图模型,其属性类型为EntityViewModel(是实体的包装器)。所有更改都会立即显示在EntityViewModel中。如果要撤消更改 - 调用方法EntityViewModel.Undo()。如果要将更改应用于实体 - 请调用EntityViewModel.Apply()。然后你可以调用方法DbContext.SaveChanges()。
public class Entity
{
public string Id { get; set; }
public string State { get; set; }
}
public class EntityViewModel : ViewModelBase
{
private string _state;
public EntityViewModel(Entity entity)
{
Entity = entity;
_state = entity.State;
}
public string State
{
get { return _state; }
set
{
if (value == _state)
return;
_state = value;
base.OnPropertyChanged("State");
}
}
public Entity Entity {get; private set; }
public void ApplyChanges()
{
Entity.State = _state;
}
public void Undo()
{
State = entity.State;
}
}
这是一个很好的职责分工,适合MVVM。