将EF5与通用存储库模式一起使用,并在尝试使用存储过程与我的edmx将数据更新到数据库时遇到依赖性损害并遇到问题。
我在DbContextRepository.cs中的更新是:
public override void Update(T entity)
{
if (entity == null)
throw new ArgumentException("Cannot add a null entity.");
var entry = _context.Entry<T>(entity);
if (entry.State == EntityState.Detached)
{
_context.Set<T>().Attach(entity);
entry.State = EntityState.Modified;
}
}
从我的AddressService.cs回到我的存储库,我有:
public int Save(vw_address address)
{
if (address.address_pk == 0)
{
_repo.Insert(address);
}
else
{
_repo.Update(address);
}
_repo.SaveChanges();
return address.address_pk;
}
当它遇到Attach和EntityState.Modified时,它会发出错误:
ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。
我已经查看了堆栈和互联网上的许多建议,但没有提出任何可以解决问题的建议。任何工作都会受到赞赏。
谢谢!
答案 0 :(得分:125)
修改:使用Find
代替Local.SingleOrDefault
的原始答案。它与@ Juan的Save
方法结合使用,但它可能导致对数据库进行不必要的查询,else
部分可能从未执行过(执行else部分会导致异常,因为Find已查询数据库但没有找到了实体,因此无法更新)。感谢@BenSwayne找到问题。
您必须检查上下文是否已跟踪具有相同密钥的实体并修改该实体,而不是附加当前实体:
public override void Update(T entity) where T : IEntity {
if (entity == null) {
throw new ArgumentException("Cannot add a null entity.");
}
var entry = _context.Entry<T>(entity);
if (entry.State == EntityState.Detached) {
var set = _context.Set<T>();
T attachedEntity = set.Local.SingleOrDefault(e => e.Id == entity.Id); // You need to have access to key
if (attachedEntity != null) {
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
} else {
entry.State = EntityState.Modified; // This should attach entity
}
}
}
正如您所看到的,主要问题是SingleOrDefault
方法需要知道查找实体的关键。您可以创建简单的界面,公开密钥(在我的示例中为IEntity
),并在您希望以此方式处理的所有实体中实现它。
答案 1 :(得分:8)
我不想通过添加接口或属性来污染我自动生成的EF类。所以这真的是上面的一些答案(所以归功于Ladislav Mrnka)。这为我提供了一个简单的解决方案。
我在update方法中添加了一个func,它找到了实体的整数键。
public void Update(TEntity entity, Func<TEntity, int> getKey)
{
if (entity == null) {
throw new ArgumentException("Cannot add a null entity.");
}
var entry = _context.Entry<T>(entity);
if (entry.State == EntityState.Detached) {
var set = _context.Set<T>();
T attachedEntity = set.Find.(getKey(entity));
if (attachedEntity != null) {
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
} else {
entry.State = EntityState.Modified; // This should attach entity
}
}
}
然后当您调用代码时,可以使用..
repository.Update(entity, key => key.myId);
答案 2 :(得分:7)
您实际上可以通过反射来检索Id,请参阅下面的示例:
var entry = _dbContext.Entry<T>(entity);
// Retreive the Id through reflection
var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);
if (entry.State == EntityState.Detached)
{
var set = _dbContext.Set<T>();
T attachedEntity = set.Find(pkey); // access the key
if (attachedEntity != null)
{
var attachedEntry = _dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // attach the entity
}
}
答案 3 :(得分:2)
@ serj-sagan你应该这样做:
**请注意,YourDb应该是从DbContext派生的类。
public abstract class YourRepoBase<T> where T : class
{
private YourDb _dbContext;
private readonly DbSet<T> _dbset;
public virtual void Update(T entity)
{
var entry = _dbContext.Entry<T>(entity);
// Retreive the Id through reflection
var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);
if (entry.State == EntityState.Detached)
{
var set = _dbContext.Set<T>();
T attachedEntity = set.Find(pkey); // access the key
if (attachedEntity != null)
{
var attachedEntry = _dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // attach the entity
}
}
}
}
答案 4 :(得分:1)
另一个解决方案(根据@ Sergey的回答)可能是:
private void Update<T>(T entity, Func<T, bool> predicate) where T : class
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
var set = Context.Set<T>();
T attachedEntity = set.Local.SingleOrDefault(predicate);
if (attachedEntity != null)
{
var attachedEntry = Context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
}
}
然后你会这样称呼它:
Update(EntitytoUpdate, key => key.Id == id)
答案 5 :(得分:0)
没有反射,如果您不想使用接口,则可以使用功能委托来查找数据库中的实体。 以下是上面的更新样本。
private void Update<T>(T entity, Func<ObservableCollection<T>, T> locatorMap) where T : class
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
var set = Context.Set<T>();
T attachedEntity = locatorMap(set.Local);
if (attachedEntity != null)
{
var attachedEntry = Context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
}
}
你会这样称呼:
Update(EntitytoUpdate, p => p.SingleOrDefault(a => a.Id == id))
答案 6 :(得分:0)
如果您将上下文设置为AsNoTracking(),则会停止aspmvc跟踪内存中实体的更改(无论如何,这都是您想要的)。
_dbContext.Products.AsNoTracking().Find(id);
了解更多相关信息
答案 7 :(得分:0)
分离找到的实体(请参阅Ladislav's解决方案中的void CMatSizeEditList::OnMouseMove(UINT nFlags, CPoint point)
{
int nRow = HitTest(point);
int nCol = ColumnFromPoint(point);
m_cToolTip.AddTool(&m_cMatSizeList, IDC_MAT_UNITS);
m_cToolTip.Activate(TRUE);
CListControl::OnMouseMove(nFlags, point);
}
BOOL CMatSizeEditList::PreTranslateMessage(MSG* pMsg)
{
m_cToolTip.RelayEvent(pMsg);
return CXCtrlDialog::PreTranslateMessage(pMsg);
}
)并重新附加已修改的实体,对我来说很合适。
这背后的原因很简单:如果某些东西是不可变的,那么将它(作为一个整体,实体)替换为它所属的东西。
以下是如何执行此操作的示例:
CListCtrl
当然,可以很容易地发现这个片段是泛型方法的一部分,因此使用attachedEntity
(模板参数)和var set = this.Set<T>();
if (this.Entry(entity).State == EntityState.Detached)
{
var attached = set.Find(id);
if (attached != null) { this.Entry(attached).State = EntityState.Detached; }
this.Attach(entity);
}
set.Update(entity);
。
答案 8 :(得分:-2)
上面的回答可能是EF 4.1+。对于那些4.0上的人,尝试这个简单的方法...没有经过实际测试,但确实附加并保存了我的更改。
public void UpdateRiskInsight(RiskInsight item)
{
if (item == null)
{
throw new ArgumentException("Cannot add a null entity.");
}
if (item.RiskInsightID == Guid.Empty)
{
_db.RiskInsights.AddObject(item);
}
else
{
item.EntityKey = new System.Data.EntityKey("GRC9Entities.RiskInsights", "RiskInsightID", item.RiskInsightID);
var entry = _db.GetObjectByKey(item.EntityKey) as RiskInsight;
if (entry != null)
{
_db.ApplyCurrentValues<RiskInsight>("GRC9Entities.RiskInsights", item);
}
}
_db.SaveChanges();
}
答案 9 :(得分:-3)
您可能忘记了在algun地方{/ 1}对象fBLL = new FornecedorBLL();