IEntityChangeTracker的多个实例不能引用实体对象.同时在Entity Framework 4.1中向实体添加相关对象

Smi*_*ily 161 c# asp.net ado.net entity-framework foreign-keys

我正在尝试保存员工详细信息,其中包含对City的引用.但每当我尝试保存我的联系人时,我都会得到例外"ADO.Net实体框架实例对象不能被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);
    }
Run Code Online (Sandbox Code Playgroud)

Employeeservice代码

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";
        }
Run Code Online (Sandbox Code Playgroud)

Sla*_*uma 235

因为这两条线......

EmployeeService es = new EmployeeService();
CityService cs = new CityService();
Run Code Online (Sandbox Code Playgroud)

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

Payroll.Entities.City city1 = cs.SelectCity(...);
Run Code Online (Sandbox Code Playgroud)

...你附上city1了上下文CityService.稍后您添加一个city1作为对new的引用Employee e1并添加,e1 包括对该city1上下文的引用EmployeeService.结果你city1附加了两个不同的上下文,这是异常所抱怨的.

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

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance
Run Code Online (Sandbox Code Playgroud)

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

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

  • 摘要ORM就像把黄色口红放在粪便上. (12认同)
  • 即使答案中没有包含一些背景信息,我也喜欢你弄明白的方式. (3认同)

Pav*_*nik 29

重现的步骤可以简化为:

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();
Run Code Online (Sandbox Code Playgroud)

代码没有错误:

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();
Run Code Online (Sandbox Code Playgroud)

  • 在某些情况下,您可能希望使用其他实例,例如指向其他数据库时. (3认同)
  • 假设您想使用contextTwo?(可能是由于范围问题或其他原因)你如何从contextOne分离并附加到contextTwo? (2认同)

小智 9

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

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}
Run Code Online (Sandbox Code Playgroud)

然后它足够分配:

e1.CityId=city1.ID;
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

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

上下文单例类:

参考: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;
    }
  }
}  
Run Code Online (Sandbox Code Playgroud)

存储库类:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]
Run Code Online (Sandbox Code Playgroud)

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

  • ...一旦你尝试使用多线程,这不会崩溃吗? (9认同)
  • 上下文不应该超过必要的时间保持打开状态,使用Singleton来永久打开它是你想要做的最后一件事. (7认同)
  • 我已经看到每个请求的良好实现.使用Static关键字是错误的,但是如果你使这个模式在请求开始时实例化上下文并在请求结束时将其处理掉,那么它将是一个合法的解决方案. (3认同)
  • 这是不好的.坏.坏.坏.坏.特别是如果这是一个Web应用程序,因为静态对象在所有线程和用户之间共享.这意味着您网站的多个并发用户将踩踏您的数据上下文,可能会破坏它,保存您不想要的更改,甚至只是创建随机崩溃.不应该跨线程共享DbContexts.然后就会出现静电不会被破坏的问题,所以它会坐下来继续使用越来越多的内存...... (3认同)
  • 这真是个糟糕的建议。如果您使用 DI(我在这里没有看到证据?),那么您应该让 DI 容器管理上下文生命周期,并且它可能应该是针对每个请求的。 (2认同)

Rom*_*n O 5

除了注入和更糟糕的单例之外,您可以在 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));
}
Run Code Online (Sandbox Code Playgroud)