在调用SaveChanges()之前,在Add()之后从上下文中检索实体

时间:2011-06-23 16:22:12

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

假设我有一个文件,其中多行代表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();

4 个答案:

答案 0 :(得分:10)

我想我可以给出同样的答案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);

并摆脱Entity Framework的奇怪行为。

答案 1 :(得分:1)

修改您的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;
}

答案 2 :(得分:0)

如果你想去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);

答案 3 :(得分:-1)

我没有找到任何关于最佳做法的内容,所以我会发布我的解决方案。我知道很多其他帖子都提到了这样一个事实,即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();