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

  显示原文与译文双语对照的内容

我正在尝试保存员工详细信息,该信息与城市有联系。 但每次我试图拯救我的接触,这是验证我除了 "ADO.Net Entity Framework 实体引用的对象不能被多个实例 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);
 }

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

时间:

因为这两条线。


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

不要在构造函数中获取参数,我猜你在类中创建了一个上下文。 加载 city1 时。


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

。将 city1 附加到 CityService 中的上下文。 稍后你添加一个 city1 作为对新 Employeee1的引用,并添加 e1 ,包括 city1EmployeeService 中的。 因此,你将 city1 附加到两个不同的上下文,这就是异常所引发的。

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


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

你的服务类看起来有点像仓库,它们只负责一个实体类型。 在这种情况下,当你为服务使用单独的上下文时,实体之间的关系就会一直有问题。

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

或者更糟糕的是,在添加前,你可以调用独立的分离方法。

EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

EntityFramework 4: cs.Detach(city1);

还有另一种方法,如果你不需要第一个DBContext对象。 就把它使用关键字:


Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
 city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}

我有同样的问题,我可以解决我试图更新的对象的新实例。 然后我把那个对象传给我的reposotory 。

这是一个旧的线程,但另一个解决方案,我更喜欢更新 cityId,而不是将hole模型城市分配给 employee 。 要做到这一点,员工应该如下所示:


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

然后就可以分配:

 
e1.CityId=city1.ID;

 

( 如果我是错的,请不要向我投票,只是解释)

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

我正在使用依赖注入来将服务/知识库层注入控制器,这样就不能从控制器访问上下文。

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

Context & 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;
 }
 }
} 

的知识库类:


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

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

复制步骤可以简化为:


var contextOne = new EntityContext();
var contextTwo = new EntityContext();

var user = contexOne.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();

在我的例子中,我使用了 ASP.NET 身份框架。 我使用了内置的UserManager.FindByNameAsync 方法来检索一个 ApplicationUser 实体。 然后我尝试在另一个 DbContext 上的新创建实体上引用这个实体。 这导致了你最初看到的异常。

我解决了这个通过创建一个新的ApplicationUser 实体只有从 UserManagerId 方法和新实体的引用。

...