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

C# 在调用SaveChanges()之前,在Add()之后从上下文检索实体,c#,.net,entity-framework,entity-framework-4,C#,.net,Entity Framework,Entity Framework 4,假设我有一个文件,其中有多行,每行代表一个人,人实体有一个标识列,该列也是主键。假设文件中可以重复一个人,如果是的话,也许我想做一个最后输入的wins场景。在下面的示例中,我使用存储库根据社会保险号码从数据库中检索一个人。问题是,当相同的SSN再次出现在文件中时,存储库仍然返回空值,即使从技术上讲,具有该SSN的人员已经添加到上下文中(SaveChanges尚未调用)。我意识到我可以通过跟踪自己来解决这个问题,因为已经添加了Person对象。我想知道这种情况下的最佳实践是什么。 谢谢 我还没有找

假设我有一个文件,其中有多行,每行代表一个
实体有一个
标识列
,该列也是
主键
。假设文件中可以重复一个
,如果是的话,也许我想做一个最后输入的wins场景。在下面的示例中,我使用存储库根据社会保险号码从数据库中检索一个人。问题是,当相同的SSN再次出现在文件中时,存储库仍然返回空值,即使从技术上讲,具有该SSN的人员已经添加到上下文中(
SaveChanges
尚未调用)。我意识到我可以通过跟踪自己来解决这个问题,因为已经添加了
Person
对象。我想知道这种情况下的最佳实践是什么。 谢谢


我还没有找到关于最佳实践的任何信息,因此我将发布我的解决方案。我知道许多其他帖子都提到了这样一个事实,即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();
Dictionary newPeople=。。。
foreach(文件行中的行)
{
个人;
//如果该人员已被词典跟踪,则使用词典,如果不使用存储库,则使用该词典
如果(!newPeople.TryGetValue(row.SSN,out person))
{
person=personRepository.GetBySSN(row.SSN);
}
if(person==null)
{
//插入逻辑
//跟踪此人是否已排队等待插入
newPeople.Add(row.SSN,person);
}
其他的
{
//更新逻辑
}
}
personRepository.SaveChanges();

修改您的
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;
}
公众人物GetBySSN(字符串ssn)
{
Person p=context.ObjectStateManager
.GetObjectStateEntries(~EntityState.Deleted)
.其中(e=>!e.IsRelationship)
.选择(e=>e.Entity)
第()类
.SingleOrDefault(p=>p.SSN=SSN);
if(p==null)
{
p=context.People.SingleOrDefault(p=>p.SSN=SSN);
}
返回p;
}

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

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

比如说

var bar = new Bar();
bar.Name = "foo";
var context = new Context();
context.Bars.Add(bar);
令人惊讶的是,查询
context.bar
,找不到刚刚添加的实体

var howMany = context.Bars.Count(b => b.Name == "foo");
// howMany == 0
context.SaveChanges()之后将产生同一行
1

DbSet
似乎不知道这些更改,直到它们在数据库中持久化

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

var howMany = context.Bars.Local.Count(b => b.Name == "foo");
// howMany == 1
您还可以使用
Local
添加实体

context.Bars.Local.Add(bar);

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

如果您想走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>();
}
公共静态IEnumerable LoadedEntities(此ObjectContext上下文)
{
返回Context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged)
.Where(e=>!e.IsRelationship)。选择(e=>e.Entity)。of type();
}
这将允许您执行以下操作:

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

重复?我恭敬地表示不同意。尽管这些都是类似的主题,但我想知道在使用存储库时的最佳实践是什么。我意识到,如果我可以直接访问EF上下文,我将能够发现是否添加了某些SSN。由于我一直在研究存储库的抽象,我想知道最佳实践是什么。谢谢Ladislav。这当然是另一种方式。如果你看一下我上面的解决方案,我会让业务逻辑跟踪已经添加的内容。因此,如果我想要未保存和已保存实体的
计数
,我必须执行
context.bar.Count+context.bar.Local.Count
?@ruudbanders No,context.bar.Local应包含未保存的实体以及数据库中已有的实体。我必须更正自己:context.bar.Local包含这两个实体,只要您之前至少查询过context.bar一次。因此,您不应该依赖context.bar.Local来获取所有数据,但也不能将这两个计数相加。
Person p = context.LoadedEntities<Person>().SingleOrDefault(p => p.SSN = ssn);
if (p != null)
    return p;
return context.People.SingleOrDefault(p => p.SSN = ssn);