đối tượng thực thể không thể được tham chiếu bởi nhiều phiên bản của IEntityChangeTracker. trong khi thêm các đối tượng liên quan vào thực thể trong Entity Framework 4.1


165

Tôi đang cố gắng lưu thông tin chi tiết về Nhân viên, có tài liệu tham khảo với Thành phố. Nhưng mỗi khi tôi cố gắng lưu liên hệ của mình, được xác thực, tôi nhận được ngoại lệ "Khung thực thể ADO.Net Một đối tượng thực thể không thể được tham chiếu bởi nhiều phiên bản của IEntityChangeTracker"

Tôi đã đọc rất nhiều bài đăng nhưng vẫn không biết chính xác phải làm gì ... mã nhấp vào nút Lưu của tôi được đưa ra dưới đây

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

mã dịch vụ nhân viên

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

Câu trả lời:


241

Bởi vì hai dòng này ...

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

... không lấy tham số trong hàm tạo, tôi đoán rằng bạn tạo bối cảnh trong các lớp. Khi bạn tải city1...

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

... bạn đính kèm city1bối cảnh vào CityService. Sau đó, bạn thêm một city1tham chiếu đến mới Employee e1và thêm e1 bao gồm tham chiếu nàycity1 vào bối cảnh trong EmployeeService. Kết quả là bạn đã city1gắn liền với hai bối cảnh khác nhau, đó là điều mà ngoại lệ phàn nàn.

Bạn có thể khắc phục điều này bằng cách tạo một bối cảnh bên ngoài các lớp dịch vụ và tiêm và sử dụng nó trong cả hai dịch vụ:

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

Các lớp dịch vụ của bạn trông hơi giống các kho lưu trữ chỉ chịu trách nhiệm cho một loại thực thể duy nhất. Trong trường hợp như vậy, bạn sẽ luôn gặp rắc rối ngay khi mối quan hệ giữa các thực thể có liên quan khi bạn sử dụng các bối cảnh riêng biệt cho các dịch vụ.

Bạn cũng có thể tạo một dịch vụ chịu trách nhiệm cho một tập hợp các thực thể có liên quan chặt chẽ, như một EmployeeCityService(có một bối cảnh duy nhất) và ủy thác toàn bộ hoạt động trong Button1_Clickphương thức của bạn cho một phương thức của dịch vụ này.


4
Tôi thích cách bạn tìm ra nó ngay cả khi câu trả lời không bao gồm một số thông tin cơ bản.
Daniel Kmak

Có vẻ như điều này sẽ giải quyết vấn đề của tôi, tôi chỉ không biết làm thế nào để viết ví dụ bối cảnh mới :(
Ortund

12
Tóm tắt ORM giống như đặt son môi màu vàng lên một chiếc máy.
Ronnie Overby

Tôi có thể đang thiếu một cái gì đó ở đây, nhưng trong một số ORM (đặc biệt là EntityFramework) bối cảnh dữ liệu phải luôn được rút ngắn. Giới thiệu một bối cảnh tĩnh hoặc được sử dụng lại sẽ giới thiệu một loạt các thách thức và vấn đề khác.
Maritim

@Maritim nó phụ thuộc vào cách sử dụng. Trong các ứng dụng web, nó thường là một vòng. Trong Ứng dụng máy tính để bàn, bạn cũng có thể sử dụng một cho mỗi Form(bao giờ, nó chỉ đại diện cho một đơn vị công việc) mỗi Thread(vì DbContextkhông được đảm bảo là an toàn cho luồng).
LuckyLikey

30

Các bước để tái tạo có thể được đơn giản hóa để này:

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

Mã không có lỗi:

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

Chỉ sử dụng một EntityContextcó thể giải quyết điều này. Tham khảo các câu trả lời khác cho các giải pháp khác.


2
giả sử bạn muốn sử dụng bối cảnh? (có thể do vấn đề phạm vi hoặc một cái gì đó) làm thế nào để bạn tách khỏi bối cảnh và gắn vào bối cảnh?
NullVoxPopuli

Nếu bạn cần làm smth như thế này, rất có thể bạn đang làm điều này sai cách ... Tôi khuyên bạn nên sử dụng một bối cảnh.
Pavel Shkleinik

3
Có những trường hợp bạn muốn sử dụng một thể hiện khác, chẳng hạn như khi trỏ vào một cơ sở dữ liệu khác.
Jay

1
Đây là một đơn giản hóa hữu ích của vấn đề; nhưng nó không cung cấp một câu trả lời thực sự.
BrainSlugs83

9

Đây là một chủ đề cũ, nhưng một giải pháp khác, mà tôi thích, chỉ là cập nhật cityId và không gán mô hình lỗ Thành phố cho Nhân viên ... để làm điều đó Nhân viên sẽ trông như sau:

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

Thế là đủ phân công:

e1.CityId=city1.ID;

5

Ngoài ra để tiêm và thậm chí tệ hơn Singleton, bạn có thể gọi phương thức Detach trước khi Thêm.

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

EntityFramework 4: cs.Detach(city1);

Vẫn còn một cách khác, trong trường hợp bạn không cần đối tượng DBContext đầu tiên. Chỉ cần bọc nó bằng cách sử dụng từ khóa:

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

1
Tôi đã sử dụng như sau: dbContext1.Entry(backgroundReport).State = System.Data.Entity.EntityState.Detached'để tách ra và sau đó có thể sử dụng dbContext2.Entry(backgroundReport).State = System.Data.Entity.EntityState.Modified;để cập nhật. Làm việc như một giấc mơ
Peter Smith

Vâng, Peter. Tôi nên đề cập để đánh dấu Nhà nước là Sửa đổi.
La Mã O

Trong logic khởi động ứng dụng (global.asax) của tôi, tôi đã tải lên một danh sách các widget .. một danh sách đơn giản các đối tượng tham chiếu mà tôi nhét vào bộ nhớ. Vì tôi đang thực hiện bối cảnh EF của mình bên trong Sử dụng các câu lệnh, tôi nghĩ rằng sẽ không có vấn đề gì sau đó khi bộ điều khiển của tôi xoay quanh việc gán các đối tượng đó vào một biểu đồ kinh doanh (hey, bối cảnh cũ đó đã biến mất, phải không?) - câu trả lời này đã cứu tôi .
bkwdesign

4

Tôi gặp vấn đề tương tự nhưng vấn đề của tôi với giải pháp của @ Slauma (mặc dù rất tuyệt trong một số trường hợp) là nó khuyên tôi nên chuyển ngữ cảnh vào dịch vụ ngụ ý rằng bối cảnh có sẵn từ bộ điều khiển của tôi. Nó cũng buộc khớp nối chặt chẽ giữa bộ điều khiển và các lớp dịch vụ của tôi.

Tôi đang sử dụng Dependency Injection để tiêm các lớp dịch vụ / kho lưu trữ vào bộ điều khiển và do đó không có quyền truy cập vào ngữ cảnh từ bộ điều khiển.

Giải pháp của tôi là để các lớp dịch vụ / kho lưu trữ sử dụng cùng một thể hiện của bối cảnh - Singleton.

Bối cảnh lớp Singleton:

Tham khảo: http://msdn.microsoft.com/en-us/l 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;
    }
  }
}  

Lớp lưu trữ:

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

Các giải pháp khác tồn tại như khởi tạo bối cảnh một lần và chuyển nó vào các hàm tạo của các lớp dịch vụ / kho lưu trữ của bạn hoặc một lớp khác mà tôi đọc về việc đang triển khai mẫu Đơn vị công việc. Tôi chắc chắn có nhiều ...


9
... không phải điều này bị hỏng ngay khi bạn thử và sử dụng đa luồng?
CaffGeek

8
Một bối cảnh không nên duy trì mở lâu hơn cần thiết, sử dụng Singleton để giữ cho nó mở mãi mãi là điều cuối cùng bạn muốn làm.
enzi

3
Tôi đã thấy việc thực hiện tốt điều này theo yêu cầu. Sử dụng từ khóa tĩnh là sai nhưng nếu bạn tạo mẫu này để khởi tạo bối cảnh khi bắt đầu yêu cầu và loại bỏ nó ở cuối yêu cầu, đó sẽ là một giải pháp hợp pháp.
Aidin

1
Đây thực sự là một lời khuyên tồi. Nếu bạn đang sử dụng DI (Tôi không thấy bằng chứng ở đây?) Thì bạn nên để bộ chứa DI của bạn quản lý vòng đời bối cảnh và nó có thể phải theo yêu cầu.
Casey

3
Điều này tệ đây. XẤU. XẤU. XẤU. XẤU. Đặc biệt nếu đây là một ứng dụng web, vì các đối tượng tĩnh được chia sẻ giữa tất cả các luồng và người dùng. Điều này có nghĩa là nhiều người dùng đồng thời của trang web của bạn sẽ dậm chân trên bối cảnh dữ liệu của bạn, có khả năng làm hỏng trang web đó, lưu các thay đổi bạn không có ý định hoặc thậm chí chỉ tạo ra các sự cố ngẫu nhiên. DbContexts KHÔNG BAO GIỜ được chia sẻ trên các chủ đề. Sau đó, có một vấn đề là các số liệu thống kê không bao giờ bị phá hủy, vì vậy nó sẽ ngồi và tiếp tục sử dụng bộ nhớ ngày càng nhiều hơn ...
Erik Funkenbusch

3

Trong trường hợp của tôi, tôi đã sử dụng Khung nhận dạng ASP.NET. Tôi đã sử dụng UserManager.FindByNameAsyncphương thức dựng sẵn để lấy một ApplicationUserthực thể. Sau đó tôi đã cố gắng tham chiếu thực thể này trên một thực thể mới được tạo trên một thực thể khác DbContext. Điều này dẫn đến ngoại lệ bạn nhìn thấy ban đầu.

Tôi đã giải quyết điều này bằng cách tạo một ApplicationUserthực thể mới chỉ bằng Idtừ UserManagerphương thức và tham chiếu thực thể mới đó.


1

Tôi có cùng một vấn đề và tôi có thể giải quyết việc tạo một thể hiện mới của đối tượng mà tôi đang cố cập nhật. Sau đó, tôi đã chuyển đối tượng đó đến reposotory của tôi.


Bạn có thể vui lòng giúp đỡ với mã mẫu. ? vì vậy nó sẽ rõ ràng những gì bạn đang cố gắng nói
BJ Patel

1

Trong trường hợp này, hóa ra lỗi rất rõ ràng: Entity Framework không thể theo dõi một thực thể bằng nhiều trường hợp IEntityChangeTrackerhoặc thông thường, nhiều phiên bản của DbContext. Các giải pháp là: sử dụng một ví dụ DbContext; truy cập tất cả các thực thể cần thiết thông qua một kho lưu trữ duy nhất (tùy thuộc vào một thể hiện của DbContext); hoặc tắt theo dõi cho tất cả các thực thể được truy cập thông qua một kho lưu trữ khác với một thực thể ném ngoại lệ cụ thể này.

Khi theo mô hình đảo ngược của API điều khiển .Net Core Web, tôi thường thấy rằng tôi có các bộ điều khiển với các phụ thuộc như:

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

và cách sử dụng như

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

Vì cả ba kho lưu trữ phụ thuộc vào các DbContextphiên bản khác nhau cho mỗi yêu cầu, tôi có hai tùy chọn để tránh sự cố và duy trì các kho lưu trữ riêng biệt: thay đổi nội dung tiêm DbContext để tạo một phiên bản mới chỉ một lần cho mỗi cuộc gọi:

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

hoặc, nếu thực thể con đang được sử dụng theo cách chỉ đọc, hãy tắt theo dõi trên trường hợp đó:

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);


0

Tôi gặp vấn đề tương tự sau khi triển khai IoC cho một dự án (ASP.Net MVC EF6.2).

Thông thường tôi sẽ khởi tạo một bối cảnh dữ liệu trong hàm tạo của bộ điều khiển và sử dụng cùng một bối cảnh để khởi tạo tất cả các kho lưu trữ của tôi.

Tuy nhiên, việc sử dụng IoC để khởi tạo các kho lưu trữ khiến tất cả chúng có bối cảnh riêng biệt và tôi bắt đầu gặp lỗi này.

Bây giờ tôi đã quay trở lại chỉ mới cập nhật các kho lưu trữ với bối cảnh chung trong khi tôi nghĩ ra một cách tốt hơn.


0

Đây là cách tôi gặp phải vấn đề này. Đầu tiên tôi cần lưu Ordercái cần tham chiếu vào ApplicationUserbảng của mình :

  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

Vấn đề là tôi đang khởi tạo ApplicationDbContext mới để lưu Orderthực thể mới của mình :

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

Vì vậy, để giải quyết vấn đề, tôi đã sử dụng cùng ApplicationDbContext thay vì sử dụng UserManager tích hợp của ASP.NET MVC.

Thay vì điều này:

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

Tôi đã sử dụng ví dụ ApplicationDbContext hiện có của mình:

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 

-2

Nguồn lỗi:

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

Hy vọng ai đó tiết kiệm thời gian quý báu


Tôi không chắc điều này trả lời câu hỏi. Có lẽ một số bối cảnh sẽ giúp.
Stuart Siegler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.