C# 实体对象不能被 IEntityChangeTracker 的多个实例引用。在 Entity Framework 4.1 中向实体添加相关对象时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10191734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
entity object cannot be referenced by multiple instances of IEntityChangeTracker. while adding related objects to entity in Entity Framework 4.1
提问by Smily
I am trying to save Employee details, which has references with City. But everytime I try to save my contact, which is validated I get the exception "ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker"
我正在尝试保存 Employee 详细信息,该详细信息与 City 相关。但是每次我尝试保存已验证的联系人时,我都会收到异常“ADO.Net 实体框架实体对象不能被 IEntityChangeTracker 的多个实例引用”
I had read so many post but still not getting the exact idea of what to do... my Save button click code is given below
我已经阅读了很多帖子,但仍然不知道该怎么做……我的保存按钮点击代码如下
protected void Button1_Click(object sender, EventArgs e)
{
EmployeeService es = new EmployeeService();
CityService cs = new CityService();
DateTime dt = new DateTime(2008, 12, 12);
Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();
Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
e1.Name = "Archana";
e1.Title = "aaaa";
e1.BirthDate = dt;
e1.Gender = "F";
e1.HireDate = dt;
e1.MaritalStatus = "M";
e1.City = city1;
es.AddEmpoyee(e1,city1);
}
and Employeeservice Code
和员工服务代码
public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
{
Payroll_DAO1 payrollDAO = new Payroll_DAO1();
payrollDAO.AddToEmployee(e1); //Here I am getting Error..
payrollDAO.SaveChanges();
return "SUCCESS";
}
采纳答案by Slauma
Because these two lines ...
因为这两行...
EmployeeService es = new EmployeeService();
CityService cs = new CityService();
... don't take a parameter in the constructor, I guess that you create a context within the classes. When you load the city1...
...不要在构造函数中使用参数,我猜您在类中创建了一个上下文。当你加载city1...
Payroll.Entities.City city1 = cs.SelectCity(...);
...you attach the city1to the context in CityService. Later you add a city1as a reference to the new Employeee1and add e1including this reference to city1to the context in EmployeeService. As a result you have city1attached to two different context which is what the exception complains about.
...您将 附加city1到 中的上下文CityService。稍后您添加 acity1作为对 new 的引用Employeee1,并将e1此引用city1添加到EmployeeService. 因此,您已city1附加到两个不同的上下文,这就是异常所抱怨的。
You can fix this by creating a context outside of the service classes and injecting and using it in both services:
您可以通过在服务类之外创建上下文并在两个服务中注入和使用它来解决此问题:
EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance
Your service classes look a bit like repositories which are responsible for only a single entity type. In such a case you will always have trouble as soon as relationships between entities are involved when you use separate contexts for the services.
您的服务类看起来有点像只负责单一实体类型的存储库。在这种情况下,当您为服务使用单独的上下文时,一旦涉及实体之间的关系,您就会总是遇到麻烦。
You can also create a single service which is responsible for a set of closely related entities, like an EmployeeCityService(which has a single context) and delegate the whole operation in your Button1_Clickmethod to a method of this service.
您还可以创建一个单一的服务,负责一组密切相关的实体,例如EmployeeCityService(具有单一上下文),并将您的Button1_Click方法中的整个操作委托给该服务的一个方法。
回答by kmullings
I had the same problem but my issue with the @Slauma's solution (although great in certain instances) is that it recommends that I pass the context into the service which implies that the context is available from my controller. It also forces tight coupling between my controller and service layers.
我遇到了同样的问题,但我对 @Slauma 解决方案的问题(尽管在某些情况下很好)是它建议我将上下文传递到服务中,这意味着上下文可从我的控制器获得。它还强制我的控制器和服务层之间紧密耦合。
I'm using Dependency Injection to inject the service/repository layers into the controller and as such do not have access to the context from the controller.
我正在使用依赖注入将服务/存储库层注入控制器,因此无法从控制器访问上下文。
My solution was to have the service/repository layers use the same instance of the context - Singleton.
我的解决方案是让服务/存储库层使用上下文的相同实例 - 单例。
Context Singleton Class:
上下文单例类:
Reference: http://msdn.microsoft.com/en-us/library/ff650316.aspx
and http://csharpindepth.com/Articles/General/Singleton.aspx
参考:http: //msdn.microsoft.com/en-us/library/ff650316.aspx
和http://csharpindepth.com/Articles/General/Singleton.aspx
public sealed class MyModelDbContextSingleton
{
private static readonly MyModelDbContext instance = new MyModelDbContext();
static MyModelDbContextSingleton() { }
private MyModelDbContextSingleton() { }
public static MyModelDbContext Instance
{
get
{
return instance;
}
}
}
Repository Class:
存储库类:
public class ProjectRepository : IProjectRepository
{
MyModelDbContext context = MyModelDbContextSingleton.Instance;
[...]
Other solutions do exist such as instantiating the context once and passing it into the constructors of your service/repository layers or another I read about which is implementing the Unit of Work pattern. I'm sure there are more...
确实存在其他解决方案,例如将上下文实例化一次并将其传递到服务/存储库层的构造函数中,或者我读到的另一个正在实现工作单元模式的方法。我确定还有更多...
回答by karolanet333
I had the same problem and I could solve making a new instance of the object that I was trying to Update. Then I passed that object to my reposotory.
我遇到了同样的问题,我可以解决为我试图更新的对象创建一个新实例。然后我将该对象传递给我的存储库。
回答by user3484623
This is an old thread, but another solution, which I prefer, is just update the cityId and not assign the hole model City to Employee... to do that Employee should look like:
这是一个旧线程,但我更喜欢的另一种解决方案是更新 cityId,而不是将孔模型 City 分配给 Employee ......这样做 Employee 应该如下所示:
public class Employee{
...
public int? CityId; //The ? is for allow City nullable
public virtual City City;
}
Then it's enough assigning:
那么分配就足够了:
e1.CityId=city1.ID;
回答by Pavel Shkleinik
Steps to reproduce can be simplified to this:
重现步骤可以简化为:
var contextOne = new EntityContext();
var contextTwo = new EntityContext();
var user = contextOne.Users.FirstOrDefault();
var group = new Group();
group.User = user;
contextTwo.Groups.Add(group);
contextTwo.SaveChanges();
Code without error:
没有错误的代码:
var context = new EntityContext();
var user = context.Users.FirstOrDefault();
var group = new Group();
group.User = user; // Be careful when you set entity properties.
// Be sure that all objects came from the same context
context.Groups.Add(group);
context.SaveChanges();
Using only one EntityContextcan solve this. Refer to other answers for other solutions.
只用一个EntityContext就可以解决这个问题。有关其他解决方案,请参阅其他答案。
回答by Roman O
Alternatively to injection and even worse Singleton, you can call Detachmethod before Add.
除了注入和更糟糕的单例之外,您可以在 Add 之前调用Detach方法。
EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);
实体框架 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);
EntityFramework 4: cs.Detach(city1);
实体框架 4: cs.Detach(city1);
There is yet another way, in case you don't need first DBContext object. Just wrap it with usingkeyword:
还有另一种方法,以防您不需要第一个 DBContext 对象。只需使用关键字包装它:
Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}
回答by Justin Skiles
In my case, I was using the ASP.NET Identity Framework. I had used the built in UserManager.FindByNameAsyncmethod to retrieve an ApplicationUserentity. I then tried to reference this entity on a newly created entity on a different DbContext. This resulted in the exception you originally saw.
就我而言,我使用的是 ASP.NET Identity Framework。我使用内置UserManager.FindByNameAsync方法来检索ApplicationUser实体。然后我尝试在另一个DbContext. 这导致了您最初看到的异常。
I solved this by creating a new ApplicationUserentity with only the Idfrom the UserManagermethod and referencing that new entity.
我通过ApplicationUser仅使用IdfromUserManager方法创建一个新实体并引用该新实体来解决此问题。
回答by Bourne Kolo
Error source:
错误来源:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ
Hope someone saves some precious time
希望有人能节省一些宝贵的时间
回答by Nalan Madheswaran
Use the same DBContext object throughout the transaction.
在整个事务中使用相同的 DBContext 对象。
回答by Kjata30
In this case, it turns out the error is very clear: Entity Framework cannot track an entity using multiple instances of IEntityChangeTrackeror typically, multiple instances of DbContext. The solutions are: use one instance of DbContext; access all needed entities through a single repository (depending on one instance of DbContext); or turning off tracking for all entities accessed via a repository other than the one throwing this particular exception.
在这种情况下,原来的错误是很清楚的:使用的多个实例,实体框架不能跟踪的实体IEntityChangeTracker或典型的多个实例DbContext。解决办法是:使用一个实例DbContext;通过单个存储库访问所有需要的实体(取决于 的一个实例DbContext);或关闭对通过存储库访问的所有实体的跟踪,而不是抛出此特定异常的实体。
When following an inversion of control pattern in .Net Core Web API, I frequently find that I have controllers with dependencies such as:
在 .Net Core Web API 中遵循控制模式反转时,我经常发现我的控制器具有依赖项,例如:
private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
IMyEntityRepository myEntityRepo,
IFooRepository fooRepo,
IBarRepository barRepo)
{
this.fooRepo = fooRepo;
this.barRepo = barRepo;
this.myEntityRepo = myEntityRepo;
}
and usage like
和用法像
...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}
...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!
Since all three repositories depend on different DbContextinstances per request, I have two options to avoid the problem and maintain separate repositories: change the injection of the DbContext to create a new instance only once per call:
由于所有三个存储库都依赖于DbContext每个请求的不同实例,因此我有两种选择来避免该问题并维护单独的存储库:更改 DbContext 的注入以每次调用仅创建一次新实例:
// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!
or, if the child entity is being used in a read-only manner, turning off tracking on that instance:
或者,如果子实体以只读方式使用,请关闭对该实例的跟踪:
myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);

