【问题标题】:Entity Framework: Re-finding objects recently added to context实体框架:重新查找最近添加到上下文中的对象
【发布时间】:2010-10-16 12:36:53
【问题描述】:

我正在使用实体框架,但在“重新查找”我刚刚创建的对象时遇到问题......基本上它是这样的:

string theId = "someId";

private void Test()
{
  using(MyEntities entities = new MyEntities())
  {
    EntityObject o = new EntityObject();
    o.Id = theId;
    entities.AddToEntityObject(o);
    CallSomeOtherMethod(entities);
  }
}

void CallSomeOtherMethod(MyEntities ents)
{
  EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId);
  if(search == null) 
  {
    Console.WriteLine("wha happened???");
  }
}

(顺便说一句,不保证代码可以正常工作 - 这都是我的想法)

为什么查询没有“找到”刚刚创建的 EntityObject?

如果我在 AddToEntityObject 之后调用 SaveChanges() 它可以工作(这并不让我感到惊讶)但是为什么它不能正确地从缓存中提取?

我对这些东西仍然很陌生,所以我希望有一些我只是忽略的非常简单的事情......

谢谢

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    新添加的对象在本地DataSource中,因为它还没有持久化到数据库中,

    所以你可能会说:

    EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ??
                          ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
    

    【讨论】:

    • 这个简单易行,应该是解决办法
    【解决方案2】:

    这是因为 ents.EntityObject.WhatEver 总是查询数据源。这是一个设计决定。他们这样做是因为否则他们将不得不对数据源、本地缓存执行查询,然后合并结果。正如其中一位开发人员在博客中指出的那样(不记得确切的位置),他们无法始终如一地处理这个问题。

    您可以想象,您必须正确处理很多角落和边缘情况。您可以在数据库中找到您在本地创建的、由其他人创建的 id。这将迫使您准备好处理(几乎)每个查询的冲突。也许他们可以制定查询本地缓存的方法和查询数据源的方法,但这也不明智。

    你可以看看Transparent Lazy Loading for Entity Framework。这取代了正常的代码生成器,并且您获得在访问时自动填充其相关实体集合和实体引用的实体。这避免了所有的

    if (!Entity.ReleatedEntities.IsLoaded)
    {
       Entity.RelatedEntities.Load();
    }
    

    代码片段。您可以查询这些集合,因为它们总是被隐式加载。但是这个解决方案也不是完美的。有一些问题。例如,如果您创建一个新实体并访问相关实体的集合,您将收到异常,因为代码无法从数据库中检索相关实体。还有一个关于数据绑定的问题,可能还有一些我不知道的问题。

    好消息是您获得了源代码并且能够自己解决问题,如果我有时间我会检查第一个问题。但我很确定它不会那么容易修复,因为如果刚刚创建的实体不是预期的行为,我预计某些情况只是没有命中数据库。

    【讨论】:

    • 感谢您的提示/更新......我将把这个问题留待一段时间,希望有人能给我更多信息......但是谢谢
    【解决方案3】:

    我也遇到了同样的情况。我写了这个扩展方法,至少对我来说解决了这个问题(我没有问题,即我的上下文中的冲突......)

        public static IEnumerable<T> WhereInclAdded<T>(this ObjectSet<T> set, Expression<Func<T, bool>> predicate)  where T : class
        {
            var dbResult = set.Where(predicate);
    
            var offlineResult = set.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(entry => entry.Entity).OfType<T>().Where(predicate.Compile());
    
            return offlineResult.Union(dbResult);
        }
    

    【讨论】:

    • 对于 dbset 我将其更改为: var offlineResult = set.Local.Where(predicate.Compile());
    • 干净漂亮的想法,而不是 WhereInclAdded,我使用了 WhereAlsoAdded ...干杯
    • 注意这里有一个微妙的细节:.Where 使用的是 IQueryable,而本地 Where 使用的是 IEnumerable。如果您在第一个中使用 .Where(expression.Compile()) ,它将改为调用 IEnumerable ,从而导致整个表被加载到内存中。如果您将此代码用作您自己的基础,请提供详细信息。
    【解决方案4】:

    下面的扩展方法是对DbSet

    public static T TryAttach<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate) where T : class
    {
         T found = dbSet.Local.SingleOrDefault(predicate.Compile());
         if (found == null) dbSet.Attach(entity);
         return found ?? entity;
    }
    

    使用方法:

    contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);
    

    顺便说一句:我喜欢泛型!

    【讨论】:

      【解决方案5】:

      我最近也遇到了同样的问题。我在提出问题 2 年后发布了这个答案,希望这段代码可以帮助有人寻找答案。

      我基本上实现了一个名为“Find”的扩展方法(正如 Alex James 所建议的那样),它的操作方式与“Where”相同,但“Find”还会检查 ObjectContext 以查看是否有任何已添加的实体满足给定谓词。这使您可以找到尚未保存到数据库中的实体。

      Find 返回一个 IQueryable(of T),以便您可以像使用任何其他 LINQ 运算符一样使用它。

      <Extension()>
      Public Function Find(Of T As Class)(ByVal OSet As ObjectSet(Of T), _
             ByVal predicate As Expression(Of Func(Of T, Boolean))) _
             As System.Linq.IQueryable(Of T)
      
          'Check the object context for Added objects first.
          Dim AddedContextObjects = OSet.Context.ObjectStateManager _
                              .GetObjectStateEntries(EntityState.Added) _
                              .Select(Function(entity) entity.Entity).OfType(Of T)()
      
      
          Dim Cpredicate = predicate.Compile
          Dim MatchingObjects As New List(Of T)
      
          For Each TObj As T In AddedContextObjects
              If Cpredicate.Invoke(TObj) Then
                  MatchingObjects.Add(TObj)
              End If
          Next
      
          'Now include a query to retrieve objects from the DB.
          Dim DBObjects = OSet.Where(predicate)
      
          If MatchingObjects.Count > 0 Then
              'We found some added objects in the context.
              'We want to return these objects as well as any Objects in DB
              'that satisfy the predicate.
              Return MatchingObjects.Union(DBObjects).AsQueryable
          Else
              'We didn't find any added objects in the context,
              'so we just return the DB query.
              Return DBObjects
          End If
      
      End Function
      

      【讨论】:

        【解决方案6】:

        您有多种选择。您可以使用另一个分部类扩展 ObjectContext,以创建自己的机制来检索最近添加的信息。

        或者您可以在 ObjectContext 上放置一个扩展方法,通过 ObjectContext.ObjectStateManager 查找“添加”ObjectStateEntries,然后使用 LINQ to Objects 查找您要查找的内容。

        【讨论】:

          【解决方案7】:

          实体框架 6

          根据EF Docs Dbset 始终查询数据库。

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

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

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

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

          下面是一个简单的带有本地数据的 sn-p:

          await dbContext.Entity
                .Where(e => e.Title.Contains("Text"))
                .LoadAsync();
          
          var locaEntities = dbContext.Entity.Local;
          
          dbContext.Entity.Add(new Entity {});
          
          // call save post atomic operation is finished.
          await dbContext.SaveChangesAsync();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-11-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多