60

我是实体框架的新手。

我已经使用 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();
}
4

12 回答 12

42

如果您知道更改发生在 EF 之外并且想要刷新特定实体的 ctxt,则可以调用ObjectContext.Refresh

datamodel.Refresh(RefreshMode.StoreWins, orders);

如果这看起来很常见,您应该在查询中禁用对象缓存:

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

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

Context.Set<Compliances>().AsNoTracking();
于 2015-05-11T14:29:09.913 回答
31

当您使用 EF 时,默认情况下每个上下文仅加载每个实体一次。第一个查询创建实体实例并将其存储在内部。任何需要具有相同键的实体的后续查询都会返回此存储的实例。如果数据存储中的值发生更改,您仍会收到包含初始查询值的实体

认真回答:

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

于 2013-05-25T14:55:57.703 回答
20

除非您重新查询上下文,否则 EF 不会加载更改。EF 查询数据库并将它们加载到对象中,它监视您对对象而不是数据库执行的更改。EF 不跟踪直接对数据库所做的更改,也永远不会跟踪。

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

要查看更改您将不得不再次执行以下行,

datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList()
于 2013-04-05T08:33:39.287 回答
9

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

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
于 2015-05-14T15:13:06.500 回答
5

我建议您在创建上下文后对所有 EntitieSet 使用一些 MergeOption,如下所示:

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 是我的上下文。

于 2014-04-02T17:30:46.690 回答
4

下面的代码帮助我的对象用新的数据库值刷新。Entry(object).Reload() 命令强制对象调用数据库值

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
于 2014-03-13T00:11:56.607 回答
2

首先,除非您只是在进行测试和开发,否则我不建议您修改系统外部的数据库。

EF DbContext 包含一个 IDisposable 接口。要释放任何缓存的数据,请手动进行 Dispose 调用或将数据库对象放在 using 块中。

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

这将确保上下文在下次使用时被清除并重新创建。确保为您的所有电话都这样做,而不仅仅是您遇到问题的电话。

于 2015-05-11T13:15:36.147 回答
2

我怀疑这里的根本问题是你DbContext的时间太长了。我可以从您正在使用的事实中HttpContext看出您有一个 Web 应用程序,使用 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);
    }
}

但是你真的应该看看依赖注入来管理 DbContext 生命周期

于 2015-05-12T14:03:23.697 回答
2

我想你需要的是GetDatabaseValues(). 它的用法如下:

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

以下信息来自msdn

当前值是实体的属性当前包含的值。原始值是查询实体时从数据库中读取的值。数据库值是它们当前存储在数据库中的值。当查询实体后数据库中的值可能已更改时,例如当另一个用户对数据库进行并发编辑时,获取数据库值很有用。

于 2015-05-14T12:18:23.287 回答
1

几个你可以做的事情。

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

就我而言,这是一个错误的连接字符串。Entity 看起来做得很好,因为它从不抱怨,直到我将上下文设为本地并且它最终给了我错误消息。

于 2019-01-28T19:45:45.593 回答
0

EF 与 find 方法的工作方式不同,该方法从上下文中提供数据。其他查询来自 db。如果对象已经在上下文中,则返回现有对象(条目中对象属性的当前值和原始值不会被覆盖数据库值)。在以下情况下对数据库执行查询:

微软链接

它由 foreach (C#) 或 For Each (Visual Basic) 语句枚举。它由集合操作枚举,例如 ToArray、ToDictionary 或 ToList。LINQ 运算符(如 First 或 Any)在查询的最外层部分指定。调用以下方法:DbSet、DbEntityEntry.Reload 和 Database.ExecuteSqlCommand 上的 Load 扩展方法。当从数据库返回结果时,上下文中不存在的对象将附加到上下文中。如果对象已经在上下文中,则返回现有对象(条目中对象属性的当前值和原始值不会被数据库值覆盖)。

执行查询时,已添加到上下文但尚未保存到数据库的实体不会作为结果集的一部分返回。要获取上下文中的数据,请参阅本地数据。

如果查询没有从数据库返回任何行,则结果将是一个空集合,而不是 null。

于 2019-07-03T17:29:12.623 回答