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