实体框架:重新查找最近添加到上下文的对象

时间:2009-03-31 00:58:01

标签: entity-framework

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

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()它会起作用(这并不让我感到惊讶),但为什么它不能正确地从缓存中拉出来?

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

由于

7 个答案:

答案 0 :(得分:23)

新添加的对象位于本地数据源中,因为它尚未保留在数据库中 所以你可能会说     EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ?? ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);

答案 1 :(得分:18)

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

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

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

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

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

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

答案 2 :(得分:18)

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

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

答案 3 :(得分:4)

下面的扩展方法是DbSet&lt;&gt;

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);
顺便说一句:我喜欢仿制药!

答案 4 :(得分:2)

我最近一直在努力解决同样的问题。我问这个问题2年后我发布了这个答案,希望这段代码可以帮助有人搜索答案。

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

Find返回IQueryable(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

答案 5 :(得分:1)

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

或者你可以在ObjectContext上放一个查找ObjectContext.ObjectStateManager查找'已添加'ObjectStateEntries的扩展方法,然后使用LINQ to Objects查找你正在查找的内容对

答案 6 :(得分:1)

实体框架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。

以下是包含本地数据的简单代码段:

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