实体框架缓存问题

时间:2013-04-05 08:02:34

标签: c# asp.net-mvc entity-framework catch-all

我是Entity Framework的新手。

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

12 个答案:

答案 0 :(得分:36)

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

datamodel.Refresh(RefreshMode.StoreWins, orders);

如果这看起来很普遍,则应在查询中禁用对象缓存:

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

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

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

答案 1 :(得分:28)

  

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

仔细回答:

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

答案 2 :(得分:20)

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

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

要查看更改,您必须再次执行以下行,

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

答案 3 :(得分: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

答案 4 :(得分:4)

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

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

答案 5 :(得分:4)

我建议您在创建上下文后对所有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是我的背景。

答案 6 :(得分:2)

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

EF DbContext包含IDisposable接口。要释放任何缓存数据,请手动执行Dispose调用,或将Database Object放在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();
            }
        }

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

答案 7 :(得分: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

答案 8 :(得分:2)

我认为你需要的是GetDatabaseValues()。它用作:

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

以下信息来自msdn

  

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

答案 9 :(得分:0)

你可以做的事情。

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

答案 10 :(得分:0)

就我而言,这是一个错误的连接字符串。实体看起来很棒,因为在我将上下文设为本地之前,它从未抱怨过,它最终给了我错误消息。

答案 11 :(得分:0)

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

Microsoft link

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

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

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