184

我正在尝试保存与 City 相关的员工详细信息。但是每次我尝试保存经过验证的联系人时,我都会收到异常“ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker”

我已经阅读了很多帖子,但仍然不知道该怎么做......我的保存按钮点击代码如下所示

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);
    }

员工服务代码

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";
        }
4

13 回答 13

253

因为这两行...

EmployeeService es = new EmployeeService();
CityService cs = new CityService();

...不要在构造函数中使用参数,我猜你在类中创建了一个上下文。当你加载city1...

Payroll.Entities.City city1 = cs.SelectCity(...);

...您将 附加city1CityService. 稍后您添加 acity1作为对 new 的引用,Employee e1并在. 结果,您附加了两个不同的上下文,这就是异常所抱怨的。e1 city1EmployeeServicecity1

您可以通过在服务类之外创建上下文并在两个服务中注入和使用它来解决此问题:

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance

您的服务类看起来有点像只负责单一实体类型的存储库。在这种情况下,当您为服务使用单独的上下文时,一旦涉及实体之间的关系,您总是会遇到麻烦。

您还可以创建一个服务,该服务负责一组密切相关的实体,例如EmployeeCityService(具有单个上下文),并将您Button1_Click方法中的整个操作委托给该服务的方法。

于 2012-04-17T15:35:29.510 回答
32

重现的步骤可以简化为:

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();

没有错误的代码:

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();

只用一个EntityContext就可以解决这个问题。有关其他解决方案,请参阅其他答案。

于 2015-01-09T12:32:29.477 回答
9

这是一个旧线程,但我更喜欢的另一个解决方案是更新 cityId 而不是将洞模型 City 分配给 Employee... 这样做 Employee 应该如下所示:

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}

然后分配就足够了:

e1.CityId=city1.ID;
于 2014-11-09T02:57:37.910 回答
5

除了注入和更糟糕的 Singleton,您可以在 Add 之前调用Detach方法。

实体框架 6:((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

实体框架 4:cs.Detach(city1);

还有另一种方法,以防您不需要第一个 DBContext 对象。只需使用关键字包装它:

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}
于 2015-02-13T11:57:39.520 回答
4

我遇到了同样的问题,但我对@Slauma 解决方案的问题(尽管在某些情况下很好)是它建议我将上下文传递到服务中,这意味着上下文可以从我的控制器中获得。它还强制我的控制器和服务层之间的紧密耦合。

我正在使用依赖注入将服务/存储库层注入控制器,因此无法从控制器访问上下文。

我的解决方案是让服务/存储库层使用相同的上下文实例——单例。

上下文单例类:

参考: 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;
    }
  }
}  

存储库类:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]

确实存在其他解决方案,例如将上下文实例化一次并将其传递给服务/存储库层的构造函数,或者我读到的另一个正在实现工作单元模式的解决方案。我敢肯定还有更多...

于 2012-12-20T17:26:24.643 回答
3

就我而言,我使用的是 ASP.NET Identity Framework。我使用内置UserManager.FindByNameAsync方法来检索ApplicationUser实体。然后,我尝试在不同DbContext. 这导致了您最初看到的异常。

我通过ApplicationUser仅使用IdfromUserManager方法创建一个新实体并引用该新实体来解决此问题。

于 2015-03-02T02:40:10.687 回答
1

我有同样的问题,我可以解决创建一个我试图更新的对象的新实例。然后我将该对象传递给我的存储库。

于 2014-06-17T21:19:34.453 回答
1

IEntityChangeTracker在这种情况下,错误非常明显:Entity Framework 无法使用DbContext. 解决方案是:使用一个实例DbContext;通过单个存储库访问所有需要的实体(取决于 的一个实例DbContext);或关闭对通过存储库访问的所有实体的跟踪,而不是抛出此特定异常的实体。

在 .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;
}

和用法一样

...
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!

由于所有三个存储库都依赖于DbContext每个请求的不同实例,因此我有两个选项可以避免该问题并维护单独的存储库:更改 DbContext 的注入以每次调用仅创建一次新实例:

// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!

或者,如果子实体以只读方式使用,则关闭对该实例的跟踪:

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);
于 2018-09-28T15:20:43.877 回答
1

在为项目(ASP.Net MVC EF6.2)实施 IoC 后,我遇到了同样的问题。

通常我会在控制器的构造函数中初始化一个数据上下文,并使用相同的上下文来初始化我的所有存储库。

然而,使用 IoC 实例化存储库导致它们都具有单独的上下文,我开始收到此错误。

现在我已经回到只是用一个共同的上下文来更新存储库,同时我想一个更好的方法。

于 2019-11-27T01:24:29.177 回答
1

这就是我遇到这个问题的方式。首先,我需要保存我Order需要引用我的ApplicationUser表的内容:

  ApplicationUser user = new ApplicationUser();
  user = UserManager.FindById(User.Identity.GetUserId());

  Order entOrder = new Order();
  entOrder.ApplicationUser = user; //I need this user before saving to my database using EF

问题是我正在初始化一个新的 ApplicationDbContext 来保存我的新Order实体:

 ApplicationDbContext db = new ApplicationDbContext();
 db.Entry(entOrder).State = EntityState.Added;
 db.SaveChanges();

所以为了解决问题,我使用了相同的ApplicationDbContext,而不是使用ASP.NET MVC内置的UserManager。

而不是这个:

user = UserManager.FindById(User.Identity.GetUserId());

我使用了现有的 ApplicationDbContext 实例:

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 
于 2020-06-13T13:25:23.047 回答
0

在整个事务中使用相同的 DBContext 对象。

于 2017-07-24T01:54:56.547 回答
0

对于我的场景,我们有一个解决方案,其中包含多个引用相同上下文的应用程序。我必须更新 unity.config 文件,将生命周期类型添加到上下文中。

<lifetime type="PerResolveLifetimeManager" />
于 2021-07-06T16:58:44.997 回答
-3

错误来源:

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

希望有人节省一些宝贵的时间

于 2015-09-09T11:50:51.320 回答