Asp.net mvc 3 首先是实体框架代码:“quot;ObjectStateManager无法跟踪具有相同密钥的多个对象。”;
在MVC3中,我首先遇到了实体框架代码的问题。我遇到了一个例外: ObjectStateManager中已存在具有相同密钥的对象。 ObjectStateManager无法跟踪具有相同属性的多个对象 钥匙 这个问题已经解决了很多次了,但在我的情况下,我很难使用任何建议的解决方案 下面是一个代码示例:Asp.net mvc 3 首先是实体框架代码:“quot;ObjectStateManager无法跟踪具有相同密钥的多个对象。”;,asp.net-mvc-3,entity-framework,repository,code-first,dbcontext,Asp.net Mvc 3,Entity Framework,Repository,Code First,Dbcontext,在MVC3中,我首先遇到了实体框架代码的问题。我遇到了一个例外: ObjectStateManager中已存在具有相同密钥的对象。 ObjectStateManager无法跟踪具有相同属性的多个对象 钥匙 这个问题已经解决了很多次了,但在我的情况下,我很难使用任何建议的解决方案 下面是一个代码示例: FestORM.SaleMethod method = new FestORM.SaleMethod { Id = 2, Name = "Test Sale Method" }; F
FestORM.SaleMethod method = new FestORM.SaleMethod
{
Id = 2,
Name = "Test Sale Method"
};
FestContext context = new FestContext();
//everything works without this line:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
context.Entry(method).State = System.Data.EntityState.Modified;
context.SaveChanges();
编辑以澄清:我正在尝试更新数据库中已存在的对象
在代码中没有记录查询的情况下,一切正常。在我的应用程序中,我的控制器正在实例化上下文,并且相同的上下文被传递到控制器使用的多个存储库——因此我不能简单地使用不同的上下文进行初始查询操作。我已经尝试从ObjectStateManager中删除跟踪的实体,但我似乎也没有成功。我正试图找出一个对这两种情况都适用的解决方案:有时我会更新ObjectStateManager跟踪的对象,有时它恰好还没有被跟踪
FWIW,我的真实存储库函数如下所示,就像上面的代码:
public void Update(T entity)
{
//works ONLY when entity is not tracked by ObjectStateManager
_context.Entry(entity).State = System.Data.EntityState.Modified;
}
public void SaveChanges()
{
_context.SaveChanges();
}
有什么想法吗?我对此已经争论太久了…显而易见的答案是,在调用之前,您实际上没有将方法对象保存到数据库中:
//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;
然而,我想这可能只是你遗漏了一点代码。
如果你让你的实体从一个抽象类继承呢
public abstract class BaseClass
{
public int Id { get; set; }
}
然后将存储库更新为
public class Repository<T> where T : BaseClass
{
.....
public void Update(T entity)
{
_context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
}
}
公共类存储库,其中T:BaseClass
{
.....
公共无效更新(T实体)
{
_context.Entry(entity).State=entity.Id==0?System.Data.EntityState.Added:System.Data.EntityState.Modified;
}
}
另外,您可能不希望设置SaleMethod的ID,而让它由数据库生成。问题还可能是因为数据库中SaleMethod对象的Id为2,然后您尝试添加另一个Id为2的SaleMethod对象。
您看到的错误源于尝试将另一个ID为2的SaleMethod对象添加到ObjectStateManager。问题在于此查询
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
将SaleMethod实体的一个实例引入上下文,然后将此代码
context.Entry(method).State = System.Data.EntityState.Modified;
将不同的实例附加到上下文。两个实例具有相同的主键,因此EF认为您正在尝试将具有相同键的两个不同实体附加到上下文。它不知道它们应该是同一个实体
如果出于某种原因,您只需要查询名称,但不想实际将完整实体带入上下文,则可以执行以下操作:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name;
如果您要做的是更新现有实体,并且该实体的所有映射属性都有值,那么最简单的方法是不运行查询,只使用:
context.Entry(method).State = System.Data.EntityState.Modified;
如果您不想更新所有属性,可能是因为您没有所有属性的值,那么在调用SaveChanges之前查询实体并设置其属性是可以接受的方法。根据您的具体要求,有几种方法可以做到这一点。一种方法是使用Property方法,如下所示:
var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();
这些博客文章包含一些可能有用的附加信息:
谢谢您的回复。是的,ID肯定是由数据库生成的。这是一个具体的更新操作。我确实遗漏了对象最初保存的部分(可能在几年前由另一段代码最初保存,等等)。问题是,只要不先执行查询,更新就可以正常工作。执行查询显然会将其添加到ObjectStateManager的跟踪中,此时我的更新会发出嘎嘎声。谢谢。(这不会让我投票支持你的回答,因为我是新来的。)问题是,我不知道在将来的更新之前我什么时候做查询。查询可能隐藏在服务层的某个地方,然后最终,当我在查询的同一个实体上执行更新时,我遇到了错误。有没有一种方法可以将对象从上下文跟踪中移除,或者获取上下文跟踪的对象,并向上下文传达我确实正在更新这一个有问题的对象?我已经在这个方向上做了一些研究,但还没有成功。这取决于查询的意图。如果查询的目的是将一个实体带入上下文,以便可以更新和保存(这是很常见的),那么您应该为此进行设计,可能是在保存之前确保该实体被带入,如果它以前没有被带入的话。如果查询的目的是出于某种原因从数据库中获取实体的某些属性,而不将该实体带入上下文,则在该查询上使用AsNoTracking,如上所示。要添加到我之前的注释中,如果要确保已引入该实体,然后在更新实体中的属性并保存之前使用Find方法。您还可以使用查询而不是Find——默认情况下,只有实体不在上下文中时,查询才会引入实体。但是Find更有效。目前我有一组属性由viewmodel返回。viewmodel还包含正在更新的项的ID。我正在用这些数据实例化一个新对象。viewmodel不包含基础模型的所有属性,因此我通过查询获取这些缺少的属性,以填充到我的新(更新)对象中。然后我尝试保存(更新)对象。相反,我只需要先执行一个查询来获取我的对象,然后使用返回的viewmodel中的信息更新该对象的属性(而不是执行相反的操作)。听起来对吗?