EF returns 旧值
EF returns old values
我在我的桌面应用程序中使用 EF6 + WPF 和 MVVM 设计模式。我还使用 Autofac 作为 DI 容器。
我阅读了很多关于 EF 上下文生命周期管理的文章,我决定只为单个视图模型实例提供一个 EF 上下文实例。我发现了几篇关于该方法的有趣文章,因此我认为这是管理 EF 上下文的唯一好方法。我使用 Autofac 来管理 EF 生命周期,所以每次创建新的视图模型时,只会创建一个新的 EF 上下文。
当然,我遇到了一个问题。我的大多数 EF 查询都运行良好,但以下查询始终 returns 旧(缓存)值。每次按下 'Execute' 按钮时我都会调用此查询,因此每个视图/视图模型有很多次执行
this.context.someTable.Where(arg => arg.value == "value").Single();
我知道我总是可以使用以下代码重新加载实体
this.context.Entry(entity).Reload();
但对我来说这不是一个好的解决方案。我还知道,如果我处理当前上下文并在下一次查询之前重新创建,我将始终收到当前值。但是这种方法与每个视图模型一个上下文的方法相冲突。
我应该修复/更改什么以避免 EF 缓存问题并仍然具有良好的性能。
以下方法强制 EF 重新查询数据库并且不缓存结果:
this.context.someTable.AsNoTracking().Where(arg => arg.value == "value").Single();
重要的方法调用是AsNoTracking
如果你使用 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。
你不应该保留上下文
我建议您放弃单一共享上下文。我最近为一个大型 WPF 应用程序做了这个。 EF 上下文被设计为一个工作单元,您应该使用它然后调用 .Dispose()
。如果您需要急切地读取关系属性,则应使用 .Include()
提示。您应该在 using
块中构建您的上下文,这样您就知道在哪里丢失了范围,并确保上下文被释放。
您会发现 EF 的性能实际上会降低,因为它需要引用其内部缓存和状态。我发现如果使用共享上下文,批量数据插入模式会恶化。 EF 的性能不如 RDBMS。
正如您所经历的,您可以保留上下文并从缓存的实体中受益,但如果这变得很痛苦,由于您的系统的性质和用户的要求,您不再真正从缓存中受益.您的支持 RDBMS 应该足够快。一旦以任何方式缓存(包括 EF 二级缓存和 ASP.NET 输出缓存),您立即需要计划如何使缓存的实体过期。这为您的编码人员增加了更多工作,并为您的系统提供了惊人的新失败方式。
例如,考虑 EF 的一个好处是关系属性的自动解析。您可以无缝地跨越数据图,直到您遇到缓存和陈旧的实体。在这种情况下,很难在检索此类实体之前使缓存过期。
但是如果您必须在更新时重新加载
如果您真的不想将体系结构更改为 Microsoft recommended/intended 方式。我建议您跟踪所有打开的上下文(在构建时添加到静态集合,在处理时删除,使用终结器模式仔细检查,以及在处理时抑制终结器),并在保存管道中实现一些通用代码(有几种方法这样做)尝试在所有打开的上下文中重新加载实体。这是一种使 EF 实体缓存过期的主动方式。这可能会影响较大集合的性能,但您可以处理实体的白名单或黑名单,而不是处理所有已保存的实体。
就个人而言,我很高兴我做出了改变(重组为短期上下文),从长远来看,在代码可维护性和系统稳定性方面有巨大的好处。
我在我的桌面应用程序中使用 EF6 + WPF 和 MVVM 设计模式。我还使用 Autofac 作为 DI 容器。
我阅读了很多关于 EF 上下文生命周期管理的文章,我决定只为单个视图模型实例提供一个 EF 上下文实例。我发现了几篇关于该方法的有趣文章,因此我认为这是管理 EF 上下文的唯一好方法。我使用 Autofac 来管理 EF 生命周期,所以每次创建新的视图模型时,只会创建一个新的 EF 上下文。
当然,我遇到了一个问题。我的大多数 EF 查询都运行良好,但以下查询始终 returns 旧(缓存)值。每次按下 'Execute' 按钮时我都会调用此查询,因此每个视图/视图模型有很多次执行
this.context.someTable.Where(arg => arg.value == "value").Single();
我知道我总是可以使用以下代码重新加载实体
this.context.Entry(entity).Reload();
但对我来说这不是一个好的解决方案。我还知道,如果我处理当前上下文并在下一次查询之前重新创建,我将始终收到当前值。但是这种方法与每个视图模型一个上下文的方法相冲突。
我应该修复/更改什么以避免 EF 缓存问题并仍然具有良好的性能。
以下方法强制 EF 重新查询数据库并且不缓存结果:
this.context.someTable.AsNoTracking().Where(arg => arg.value == "value").Single();
重要的方法调用是AsNoTracking
如果你使用 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。
你不应该保留上下文
我建议您放弃单一共享上下文。我最近为一个大型 WPF 应用程序做了这个。 EF 上下文被设计为一个工作单元,您应该使用它然后调用 .Dispose()
。如果您需要急切地读取关系属性,则应使用 .Include()
提示。您应该在 using
块中构建您的上下文,这样您就知道在哪里丢失了范围,并确保上下文被释放。
您会发现 EF 的性能实际上会降低,因为它需要引用其内部缓存和状态。我发现如果使用共享上下文,批量数据插入模式会恶化。 EF 的性能不如 RDBMS。
正如您所经历的,您可以保留上下文并从缓存的实体中受益,但如果这变得很痛苦,由于您的系统的性质和用户的要求,您不再真正从缓存中受益.您的支持 RDBMS 应该足够快。一旦以任何方式缓存(包括 EF 二级缓存和 ASP.NET 输出缓存),您立即需要计划如何使缓存的实体过期。这为您的编码人员增加了更多工作,并为您的系统提供了惊人的新失败方式。
例如,考虑 EF 的一个好处是关系属性的自动解析。您可以无缝地跨越数据图,直到您遇到缓存和陈旧的实体。在这种情况下,很难在检索此类实体之前使缓存过期。
但是如果您必须在更新时重新加载
如果您真的不想将体系结构更改为 Microsoft recommended/intended 方式。我建议您跟踪所有打开的上下文(在构建时添加到静态集合,在处理时删除,使用终结器模式仔细检查,以及在处理时抑制终结器),并在保存管道中实现一些通用代码(有几种方法这样做)尝试在所有打开的上下文中重新加载实体。这是一种使 EF 实体缓存过期的主动方式。这可能会影响较大集合的性能,但您可以处理实体的白名单或黑名单,而不是处理所有已保存的实体。
就个人而言,我很高兴我做出了改变(重组为短期上下文),从长远来看,在代码可维护性和系统稳定性方面有巨大的好处。