在没有跟踪的情况下更新时保留 Entity Framework 中的导航属性
Preserving navigation properties in Entity Framework when updating without tracking
我正在按照 Repository 模式开发一个程序,该模式涉及使用 Entity Framework 进行数据库访问,但我遇到了一个问题。当我尝试在同一上下文中两次操作同一变量时,我遇到了这个异常:
System.InvalidOperationException: Attaching an entity of type
'Syn.Commons.Data.Tests.EntityFramework.Helper.DbModel.DBBox' failed because
another entity of the same type already has the same primary key value.
This can happen when using the 'Attach' method or [...]
我知道这是因为实体在同一上下文中被多次使用。但是,当我尝试使用 Detach()
方法时,它会丢失所有导航属性(即,所有这些 变成 null
edit : 重置为最后已知值), as stated here(分离对象部分):
In an independent association, the relationship information is not maintained for a detached object.
如果我进行查询,我会使用 AsNoTracking()
,但这是不可能的,因为我没有使用它们。这是 Update()
方法:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Set<TDbType>().Attach(dbEnt);
// The following gives an error when manipulating the entity for the second time
//_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
// The following makes the entity "lose" all of its navigation properties
//DetachEntry(_context.Entry(dbEnt));
return true;
}
这两种解决方案似乎相互冲突。有没有办法在保留其导航属性的同时不跟踪实体(没有显式查询)?
编辑: 评论中的人是对的。虽然没有坏处,但我不需要Attach()
。因此,该方法现在看起来像这样:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
return true;
}
我找到了问题的答案。
Entity Framework 从分离的实体更新时不管理关系(有关详细信息,请参阅here and 的答案),因此必须这样做手动,这有点难过,因为添加时默认管理关系。
我找到了一个库,它应该自动 link 分离实体的更新关系(这正是我想要的),但我似乎无法让它工作。叫GraphDiff,如果你想看的话(那边还有很多,你也可以搜索)。
感谢评论中的每一个人。
编辑: 经过努力,我成功地解决了我的问题。这是一对多关系的伪代码。假设您可以访问当前实体(我将其称为 entity
),并且该实体中有一个包含 "many" 部分的列表:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.OneToManyList.Except( previous.OneToManyList ); // ToList() optional but very recommended
var deleted = previous.OneToManyList.Except( entity.OneToManyList ); // Same as above
// Make the appropriate changes
foreach( var item in deleted )
{
item.TheForeignKey = null; // Foreign key related with the entity
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
item.TheForeignKey = entity.ThePrimaryKey;
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
应该是这样。现在,关于多对多关系,它变得有点棘手。我所做的是将新实体的列表恢复到它们的原始值(数据库中的那个),Attach()
实体到上下文,以便 Entity Framework 可以跟踪对关系所做的更改,并且然后更改它们:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.ManyToManyList.Except( previous.ManyToManyList ); // ToList() optional but very recommended
var deleted = previous.ManyToManyList.Except( entity.ManyToManyList ); // Same as above
// Restore the current list to the value held in the database
entity.ManyToManyList.Clear();
foreach( var item in previous.ManyToManyList )
{
// You may want to insert the line below to avoid attaching both the previous entity and the current one to the context, it gets messy then
item.OtherManyToManyList.Remove(previous);
entity.ManyToManyList.Add(item);
}
// The new entity now has a copy of the previous list
//Attach the entity with the previous list
myContext.Set<ClassOfEntity>().Attach(entity);
// Entity Framework will now acknowledge the changes made in the list and automatically create the appropriate relationships
foreach( var item in deleted )
{
entity.ManyToManyList.Remove(item);
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
entity.ManyToManyList.Add(item);
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
多对多部分并不漂亮,但很管用。您可能已经注意到,如果您有许多多对多 (hehe) 关系,您将需要使用 previous
的所有列表更新当前 entity
,然后调用 Attach()
最后使用列表。
如果没有"attaching the entity to the context with the previous list"还有其他方法可以做到,请在评论中告诉我,将不胜感激。不管怎样,希望对大家有用。
我正在按照 Repository 模式开发一个程序,该模式涉及使用 Entity Framework 进行数据库访问,但我遇到了一个问题。当我尝试在同一上下文中两次操作同一变量时,我遇到了这个异常:
System.InvalidOperationException: Attaching an entity of type
'Syn.Commons.Data.Tests.EntityFramework.Helper.DbModel.DBBox' failed because
another entity of the same type already has the same primary key value.
This can happen when using the 'Attach' method or [...]
我知道这是因为实体在同一上下文中被多次使用。但是,当我尝试使用 Detach()
方法时,它会丢失所有导航属性(即,所有这些 变成 edit : 重置为最后已知值), as stated here(分离对象部分):null
In an independent association, the relationship information is not maintained for a detached object.
如果我进行查询,我会使用 AsNoTracking()
,但这是不可能的,因为我没有使用它们。这是 Update()
方法:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Set<TDbType>().Attach(dbEnt);
// The following gives an error when manipulating the entity for the second time
//_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
// The following makes the entity "lose" all of its navigation properties
//DetachEntry(_context.Entry(dbEnt));
return true;
}
这两种解决方案似乎相互冲突。有没有办法在保留其导航属性的同时不跟踪实体(没有显式查询)?
编辑: 评论中的人是对的。虽然没有坏处,但我不需要
Attach()
。因此,该方法现在看起来像这样:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
return true;
}
我找到了问题的答案。
Entity Framework 从分离的实体更新时不管理关系(有关详细信息,请参阅here and
我找到了一个库,它应该自动 link 分离实体的更新关系(这正是我想要的),但我似乎无法让它工作。叫GraphDiff,如果你想看的话(那边还有很多,你也可以搜索)。
感谢评论中的每一个人。
编辑: 经过努力,我成功地解决了我的问题。这是一对多关系的伪代码。假设您可以访问当前实体(我将其称为
entity
),并且该实体中有一个包含 "many" 部分的列表:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.OneToManyList.Except( previous.OneToManyList ); // ToList() optional but very recommended
var deleted = previous.OneToManyList.Except( entity.OneToManyList ); // Same as above
// Make the appropriate changes
foreach( var item in deleted )
{
item.TheForeignKey = null; // Foreign key related with the entity
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
item.TheForeignKey = entity.ThePrimaryKey;
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
应该是这样。现在,关于多对多关系,它变得有点棘手。我所做的是将新实体的列表恢复到它们的原始值(数据库中的那个),Attach()
实体到上下文,以便 Entity Framework 可以跟踪对关系所做的更改,并且然后更改它们:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.ManyToManyList.Except( previous.ManyToManyList ); // ToList() optional but very recommended
var deleted = previous.ManyToManyList.Except( entity.ManyToManyList ); // Same as above
// Restore the current list to the value held in the database
entity.ManyToManyList.Clear();
foreach( var item in previous.ManyToManyList )
{
// You may want to insert the line below to avoid attaching both the previous entity and the current one to the context, it gets messy then
item.OtherManyToManyList.Remove(previous);
entity.ManyToManyList.Add(item);
}
// The new entity now has a copy of the previous list
//Attach the entity with the previous list
myContext.Set<ClassOfEntity>().Attach(entity);
// Entity Framework will now acknowledge the changes made in the list and automatically create the appropriate relationships
foreach( var item in deleted )
{
entity.ManyToManyList.Remove(item);
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
entity.ManyToManyList.Add(item);
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
多对多部分并不漂亮,但很管用。您可能已经注意到,如果您有许多多对多 (hehe) 关系,您将需要使用 previous
的所有列表更新当前 entity
,然后调用 Attach()
最后使用列表。
如果没有"attaching the entity to the context with the previous list"还有其他方法可以做到,请在评论中告诉我,将不胜感激。不管怎样,希望对大家有用。