C# ObjectStateManager中已存在具有相同密钥的对象。ObjectStateManager无法跟踪具有相同密钥的多个对象
使用带有通用存储库模式的EF5和用于依赖项插入的ninject,并在尝试使用my edmx的存储过程将实体更新到数据库时遇到问题 我在DbContextRepository.cs中的更新是:C# ObjectStateManager中已存在具有相同密钥的对象。ObjectStateManager无法跟踪具有相同密钥的多个对象,c#,entity-framework,ninject,repository-pattern,entity-framework-5,C#,Entity Framework,Ninject,Repository Pattern,Entity Framework 5,使用带有通用存储库模式的EF5和用于依赖项插入的ninject,并在尝试使用my edmx的存储过程将实体更新到数据库时遇到问题 我在DbContextRepository.cs中的更新是: public override void Update(T entity) { if (entity == null) throw new ArgumentException("Cannot add a null entity."); var entry = _contex
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;
}
}
当它点击Attach和EntityState.Modified时,它会吐出以下错误:
ObjectStateManager中已存在具有相同密钥的对象。ObjectStateManager无法使用同一密钥跟踪多个对象。
我浏览了stack和Internet上的许多建议,但没有找到任何解决方法。任何解决办法都将不胜感激
谢谢 编辑:使用原始答案
查找
,而不是本地。单一或默认
。它与@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
}
}
}
公共覆盖无效更新(T实体),其中T:entity{
if(实体==null){
抛出新ArgumentException(“无法添加空实体”);
}
var entry=_context.entry(实体);
if(entry.State==EntityState.Detached){
var set=_context.set();
T attachedEntity=set.Local.SingleOrDefault(e=>e.Id==entity.Id);//您需要有权访问密钥
如果(附件身份!=null){
var attachedEntry=_context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(实体);
}否则{
entry.State=EntityState.Modified;//这应该附加实体
}
}
}
正如您所看到的,主要问题是
SingleOrDefault
方法需要知道查找实体的键。您可以创建一个简单的接口来公开密钥(在我的示例中为IEntity
),并在您希望以这种方式处理的所有实体中实现它。上述答案可能是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();
}
public void UpdateRiskInsight(风险洞察项目)
{
如果(项==null)
{
抛出新ArgumentException(“无法添加空实体”);
}
if(item.RiskInsightID==Guid.Empty)
{
_db.RiskInsights.AddObject(项目);
}
其他的
{
item.EntityKey=新的System.Data.EntityKey(“GRC9Entities.RiskInsights”、“RiskInsightID”、item.RiskInsightID);
var entry=_db.GetObjectByKey(item.EntityKey)作为RiskInsight;
if(条目!=null)
{
_db.ApplyCurrentValue(“GRC9Entities.RiskInsights”,项目);
}
}
_db.SaveChanges();
}
您可以通过反射实际检索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
}
}
var entry=\u dbContext.entry(实体);
//通过反射检索Id
var pkey=_dbset.Create().GetType().GetProperty(“Id”).GetValue(实体);
if(entry.State==EntityState.Detached)
{
var set=_dbContext.set();
T attachedEntity=set.Find(pkey);//访问密钥
如果(附件身份!=null)
{
var attachedEntry=_dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(实体);
}
其他的
{
entry.State=EntityState.Modified;//附加实体
}
}
@您应该这样做:
**请注意,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
}
}
}
公共抽象类yourrepbase其中T:class
{
私有YourDb_dbContext;
私有只读数据库集_DbSet;
公共虚拟无效更新(T实体)
{
var entry=_dbContext.entry(实体);
//通过反射检索Id
var pkey=_dbset.Create().GetType().GetProperty(“Id”).GetValue(实体);
if(entry.State==EntityState.Detached)
{
var set=_dbContext.set();
T attachedEntity=set.Find(pkey);//访问密钥
如果(附件身份!=null)
{
var attachedEntry=_dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(实体);
}
其他的
{
entry.State=EntityState.Modified;//附加实体
}
}
}
}没有反射,如果不想使用接口,可以使用功能委托在数据库中查找实体。 这是上面更新的示例
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
}
}
}
我不想通过添加接口或属性来污染我自动生成的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
}
}
}
如果您将上下文设置为AsNoTracking(),这将停止aspmvc跟踪内存中实体的更改(这是您在web上想要的) 我建议您在上阅读更多关于这方面的内容,另一个解决方案(基于@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
}
}
}
您可能忘记安装对象
fBLL=newfornecedorbll()代码>在algun place中分离找到的实体(请参见解决方案中的AttacheIdentity
)并重新连接修改后的实体对我来说效果很好
这背后的原因是
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);
_dbContext.Products.AsNoTracking().Find(id);
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)
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);