【问题标题】:Retrieving entity from context after Add() yet before SaveChanges() has been called在 Add() 之后但在调用 SaveChanges() 之前从上下文中检索实体
【发布时间】:2011-06-23 16:22:12
【问题描述】:

假设我有一个包含多行的文件,每行代表一个PersonPerson 实体有一个identity column,它也是primary key。假设 Person 可以在文件中重复,如果是,也许我想做最后一个条目获胜的场景。在下面的示例中,我使用存储库通过社会安全号码从数据库中检索一个人。问题是,当相同的 SSN 再次出现在文件中时,存储库仍然返回 null,即使从技术上讲,具有该 SSN 的人已经被添加到上下文中(SaveChanges 尚未被调用)。我意识到我可以通过跟踪自己已经添加了哪些Person 对象来解决这个问题。我想知道这种情况的最佳做法是什么。 谢谢。

foreach(row in fileRows)
{
    Person person = personRepository.GetBySSN(row.SSN);

    if(person == null)
    {
        //insert logic
    }
    else
    {
        //update logic
    }
}

personRepository.SaveChanges();

【问题讨论】:

标签: c# .net entity-framework entity-framework-4


【解决方案1】:

我想我可以给出与here相同的答案

要持久化一个实体,您通常将其添加到上下文中的DbSet

例如

var bar = new Bar();
bar.Name = "foo";
var context = new Context();
context.Bars.Add(bar);

奇怪,查询context.Bars,找不到刚刚添加的实体

var howMany = context.Bars.Count(b => b.Name == "foo");
// howMany == 0

context.SaveChanges() 之后的同一行将产生1

DbSet 似乎不知道更改直到它们被持久化到 db 上。

幸运的是,每个DbSet 都有一个Local 属性,其作用类似于DbSet 本身,但它反映了所有内存中的操作

var howMany = context.Bars.Local.Count(b => b.Name == "foo");
// howMany == 1

您也可以使用Local添加实体

context.Bars.Local.Add(bar);

并摆脱实体框架的怪异行为。

【讨论】:

  • 因此,如果我想要未保存和已保存实体的 Count,我将不得不这样做 context.Bars.Count + context.Bars.Local.Count?
  • @RuudLenders 不,context.Bars.Local 应该包含未保存的实体以及数据库中已经存在的实体。
  • 我必须纠正自己:context.Bars.Local 包含两者,只要您之前至少查询过一次 context.Bars。所以你不应该依赖 context.Bars.Local 来拥有所有数据,但你也不能只总结这两个计数。
【解决方案2】:

修改你的GetBySSN如下:

public Person GetBySSN(string ssn) 
{
    Person p = context.ObjectStateManager
                      .GetObjectStateEntries(~EntityState.Deleted)
                      .Where(e => !e.IsRelationship)
                      .Select(e => e.Entity)
                      .OfType<Person>()
                      .SingleOrDefault(p => p.SSN = ssn);

    if (p == null) 
    {
        p = context.People.SingleOrDefault(p => p.SSN = ssn);
    }

    return p;
}

【讨论】:

  • 谢谢拉迪斯拉夫。这当然是另一种解决方法。如果您查看我上面的解决方案,我会为业务逻辑分配跟踪已添加内容的任务。
【解决方案3】:

如果您想走 Ladislav 的路线并查询 ObjectStateManager,那么您可能希望使用如下扩展方法:

public static IEnumerable<TEntity> LoadedEntities<TEntity>(this ObjectContext Context)
{
    return Context.ObjectStateManager
        .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged)
        .Where(e => !e.IsRelationship).Select(e => e.Entity).OfType<TEntity>();
}

这将允许您执行以下操作:

Person p = context.LoadedEntities<Person>().SingleOrDefault(p => p.SSN = ssn);
if (p != null)
    return p;
return context.People.SingleOrDefault(p => p.SSN = ssn);

【讨论】:

    【解决方案4】:

    我没有找到任何有关最佳做法的信息,因此我将发布我的解决方案。我知道许多其他帖子都提到了这样一个事实,即 EF 上下文提供了查看它并查看特定实体是否处于附加状态的机制。由于我通过存储库工作(我的业务层无法直接访问 EF 上下文),我的选择是将这种逻辑卸载到存储库中,或者尝试在业务层中解决它。我的感觉是,这项任务实际上是一个业务问题,而不是数据访问层问题。

    Dictionary<string, Person> newPeople = ...
    
    foreach(row in fileRows)
    {
        Person person;
    
        //if the person is already tracked by the dictionary, work with it, if not use the repository
        if(!newPeople.TryGetValue(row.SSN, out person))
        {
            person = personRepository.GetBySSN(row.SSN);
        }
    
        if(person == null)
        {
            //insert logic
    
            //keep track that this person is already in line for inserting
            newPeople.Add(row.SSN, person);
        }
        else
        {
            //update logic
        }
    }
    
    personRepository.SaveChanges();
    

    【讨论】:

      猜你喜欢
      • 2013-07-05
      • 1970-01-01
      • 2012-10-07
      • 1970-01-01
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多