2013-04-05 209 views
39

我是实体框架的新手。实体框架缓存问题

我已经在我的数据库中使用EF得到了一些值。它完美地返回,并且值显示在标签中。但是,当我删除我的表中的所有值(不使用EF),EF查询返回我的旧值。我知道EF将这些值存储在缓存中,并为随后的运行返回缓存的数据。它是否正确?

那么我怎样才能解决问题,当我删除我的数据库中的所有值,但EF返回旧值?

编辑

现在我使用datamodel.SaveChanges()。但现在它返回相同的旧值。

我的示例查询看起来象下面这样:除非你重新查询上下文

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities(); 
datamodel.SaveChanges(); 
List<Compliance> compliance=new List<Compliance>(); 
IList<ComplianceModel> complianceModel; 
if (HttpContext.Current.User.IsInRole("SuperAdmin")) 
{ 
    compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList(); 
} 
+3

您是否在您的dbContext上调用了'.SaveChanges();'如果你没有这些实体将不会被删除.. – Rob 2013-04-05 08:04:09

+0

请阅读我的完整问题。我没有在代码中使用删除。我直接删除了数据库。数据库 – 2013-04-05 08:05:49

+0

您的上下文如何?当你需要时打开上下文,一旦完成就关闭它。 (您可能想要改进您的问题,但一点也不清楚) – 2013-04-05 08:07:18

回答

25

如果您知道变化发生EF之外,并希望刷新您的特定实体ctxt,您可以拨打ObjectContext.Refresh

datamodel.Refresh(RefreshMode.StoreWins, orders); 

如果这看起来这将是一个普遍的现象,你应该在查询中禁用对象缓存:

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities(); 
datamodel.tblCities.MergeOption = MergeOption.NoTracking; 

或关闭对象级缓存为特定的实体,

Context.Set<Compliances>().AsNoTracking(); 
+3

.AsNoTracking()对我来说 – nadav 2016-09-23 13:03:36

+1

如果使用AsNoTracking(),那么DBContext将不会跟踪对Enetities的更改。在这种情况下,您将无法使用dbContext.SaveChanges()修改实体并保存它们。 – 2017-05-03 19:08:22

+1

http://codethug.com/2016/02/19/Entity-Framework-Cache-Busting/ – 2017-05-03 19:11:15

20

EF不会加载的变化。 EF查询数据库和加载将它们映射到对象中,它监视您在对象上执行的更改,而不是数据库上执行的更改。 EF不会跟踪直接对数据库所做的更改,并且它不会跟踪。

你已经加载了一个List,List是你在内存中的缓存。即使调用保存更改也不会刷新。您将不得不再次查询上下文,即创建新列表。

要看到改变,你必须执行以下行一次,

datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList() 
25

当你每方面使用EF默认情况下它负载的每一个实体只有一次。 第一个查询创建实体实例并将其存储在内部。随后的查询要求具有相同密钥的实体返回此 存储的实例。如果数据存储值改变您仍然收到 的实体值从初始查询

一个谨慎的回答:

https://stackoverflow.com/a/3653392/1863179

+8

短版:“永远不要使用全局上下文” – Colin 2015-05-12 13:38:36

+1

我一直在调试像疯了一样,进入SSMS并检查我能找到的所有东西为什么某些电话没有被更新......现在我知道了!谢谢! – RemarkLima 2016-05-19 20:03:47

4

下面的代码帮助我的对象,与新鲜的数据库值被刷新。该条目(对象).Reload()命令强制对象召回数据库值

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName); 
DatabaseObjectContext.Entry(member).Reload(); 
4

,我建议你使用一些MergeOption所有EntitieSet创建上下文后,像这样:

var objSetProps = ctx.GetType().GetProperties().Where(prop => prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ObjectSet<>)); 
foreach (PropertyInfo objSetProp in objSetProps) 
{ 
    ObjectQuery objSet = (ObjectQuery)objSetProp.GetValue(ctx, BindingFlags.GetProperty, null, null, null); 
    objSet.MergeOption = MergeOption.PreserveChanges; 
} 

阅读关于MergeOption在这里:http://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption.aspx 你会使用NoTracking,我想。

但是,如果您清除了“缓存”的实体,则将其分开。

var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); 
foreach (var objectStateEntry in entidades) 
    Ctx.Detach(objectStateEntry.Entity); 

其中Ctx是我的上下文。

2

首先,我不会建议修改数据库外部的系统,除非你只做测试和开发。

EF DbContext包含一个IDisposable接口。要释放任何缓存数据,请手动创建Dispose调用或将数据库对象放入使用块中。

 using (SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities()) 
     { 
      List<Compliance> compliance = new List<Compliance>(); 
      IList<ComplianceModel> complianceModel; 
      if (HttpContext.Current.User.IsInRole("SuperAdmin")) 
      { 
       compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList(); 
      } 
     } 

这将确保上下文在下次使用时被清除并重新创建。请务必为您的所有来电执行此操作,而不仅仅是您遇到的问题。

2

我怀疑这里的根本问题是你的DbContext挂了太久。我可以从你正在使用HttpContext,你有一个Web应用程序的事实告诉,并General guidelines when working with DbContext包括

当使用Web应用程序的工作,每 要求使用上下文实例。

如果您正在使用MVC,你可以使用Dispose模式在你的控制器是这样的:

public class EmployeeController : Controller 
{ 
    private EmployeeContext _context; 

    public EmployeeController() 
    { 
     _context = new EmployeeContext(); 
    } 

    public ActionResult Index() 
    { 
     return View(_context.Employees.ToList()); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      _context.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
} 

但你真的应该寻找在依赖注入manage the DbContext lifetime

0

夫妇的事情,你可以做。

  1. 使用新的上下文。缓存的实体存储在上下文中。使用新的上下文会阻止它使用缓存。
  2. 如果你真的想要一个全局/持久的上下文,你有两个子选项: a。)总是调用Reload方法。 db.Entry(entity).Reload()...这会强制上下文重新加载该实体。 b。)使用SqlDependency对象来检测记录何时根据需要更改和重新加载实体。 https://code.msdn.microsoft.com/How-to-use-SqlDependency-5c0da0b3
2

我觉得你需要的是GetDatabaseValues()。它用于这样的:下面

context.Entry(/*your entry*/).GetDatabaseValues(); 

信息是从msdn

的电流值是该实体 的性质目前包含的值。原始值是查询实体时从数据库中读取的值 。数据库值为 当前存储在数据库中的值。当数据库中的值可能有 由于查询实体而发生更改,例如当数据库由另一个用户并发编辑到 时,获取 数据库值很有用。

7

我认为你应该遵循这里的一些其他解决方案,但它似乎是你想清除缓存。您可以通过执行以下操作来实现此目的:

var count = datamodel.Compliances.Local.Count; // number of items in cache (ex. 30) 

datamodel.Compliances.Local.ToList().ForEach(c => { 
    datamodel.Entry(c).State = EntityState.Detached; 
}); 

count = datamodel.Compliances.Local.Count; // 0 
+0

这是解决问题的最佳解决方案。只需清除DbContext的缓存,就可以开始了。 – 2017-06-20 05:22:56