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