有人可以向我解释为什么我下面的EF(4.3)代码第一个代码导致检索“旧”密码的原因。

using (var context = new CableSenseInstanceConfiguratorContext())
{
    var user = context.Installers.Where(u => u.UserName == "admin").FirstOrDefault();
    Console.WriteLine(user.Password); // Outputs "oldpassword"

    // Change the details on a different context;
    using (var context2 = new CableSenseInstanceConfiguratorContext())
    {
        var installer = context2.Installers.Single(i => i.UserName == "admin");
        installer.Password = "changed";
        context2.SaveChanges();
    }

    var user2 = context.Installers.Where(u => u.UserName == "admin").FirstOrDefault();
    Console.WriteLine(user2.Password); // Outputs "oldpassword"
}


密码是“ oldpassword”启动的。因此,我在另一个上下文(context2)中更改了密码,然后再次将其提取到user2中。我可以验证两者的输出均为“ oldpassword”。通过对SQL进行概要分析,我可以看到密码确实得到了更改,还可以看到,填充user2的代码正在进入数据库,但它没有使用这些值。

我知道EF具有将本地上下文作为缓存和跟踪实体的方式的概念,但是据我了解,context.Installers.Where(..)应该从数据库中强制进行重新取回,而context.Installers.Find()应该在本地上下文中进行查看。似乎无论我如何查询安装程序,它都在使用本地缓存。

编辑

感谢@Reinard提供解决方案。我误会了文档-我从here中读取:


  请注意,DbSet和IDbSet总是针对数据库创建查询
  并且始终会涉及到数据库的往返行程,即使
  返回的实体已经存在于上下文中。


因此,我假设因为它将进入数据库,所以它将重新获取我的对象。实际发生的事情是它进入了数据库,获取了对象,发现我已经在跟踪该对象(由于先前的加载),所以我最终遇到了旧对象-这实际上就是文档所说的!

使用context.Installers.Local.Clear()并没有什么不同,我需要AsNoTracking().

最佳答案

WhereFind都不会从数据库中显式地重新获取。据我所知,仅当上下文中不存在该实体时,Find才会从数据库中检索该实体。

为了显式强制执行refetch,请使用AsNoTracking()。

例如

context.Installers.AsNoTracking().Where(u => u.UserName == "admin").FirstOrDefault();

10-07 19:40
查看更多