Giải quyết “Thể hiện ObjectContext đã bị xử lý và không còn có thể được sử dụng cho các hoạt động yêu cầu kết nối” InvalidOperationException


123

Tôi đang cố điền vào một GridViewEntity Frameworkm nhưng lần nào tôi cũng gặp lỗi sau:

"Trình truy cập thuộc tính 'LoanProduct' trên đối tượng 'COSIS_DAL.MemberLoan' đã đưa ra ngoại lệ sau: Thể hiện ObjectContext đã bị xử lý và không còn có thể được sử dụng cho các hoạt động yêu cầu kết nối."

Mã của tôi là:

public List<MemberLoan> GetAllMembersForLoan(string keyword)
{
    using (CosisEntities db = new CosisEntities())
    {
        IQueryable<MemberLoan> query = db.MemberLoans.OrderByDescending(m => m.LoanDate);
        if (!string.IsNullOrEmpty(keyword))
        {
            keyword = keyword.ToLower();
            query = query.Where(m =>
                  m.LoanProviderCode.Contains(keyword)
                  || m.MemNo.Contains(keyword)
                  || (!string.IsNullOrEmpty(m.LoanProduct.LoanProductName) && m.LoanProduct.LoanProductName.ToLower().Contains(keyword))
                  || m.Membership.MemName.Contains(keyword)
                  || m.GeneralMasterInformation.Description.Contains(keyword)

                  );
        }
        return query.ToList();
    }
}


protected void btnSearch_Click(object sender, ImageClickEventArgs e)
{
    string keyword = txtKeyword.Text.ToLower();
    LoanController c = new LoanController();
    List<COSIS_DAL.MemberLoan> list = new List<COSIS_DAL.MemberLoan>();
    list = c.GetAllMembersForLoan(keyword);

    if (list.Count <= 0)
    {
        lblMsg.Text = "No Records Found";
        GridView1.DataSourceID = null;
        GridView1.DataSource = null;
        GridView1.DataBind();
    }
    else
    {
        lblMsg.Text = "";
        GridView1.DataSourceID = null;   
        GridView1.DataSource = list;
        GridView1.DataBind();
    }
}

Lỗi đang đề cập đến LoanProductNamecột của Gridview. Đã đề cập: Tôi đang sử dụng C #, ASP.net, SQL-Server 2008 làm DB cuối.

Tôi còn khá mới đối với Entity Framework. Tôi không thể hiểu tại sao tôi lại nhận được lỗi này. Có ai có thể giúp tôi không?


1
Bạn có đang truy cập bất kỳ thuộc tính điều hướng nào trong gridview không. Nếu bạn làm vậy, bạn cũng cần bao gồm các bảng điều hướng đó trong truy vấn. Thíchquery.Include("SomeOtherTable")
Nilesh

Hãy thử tạo một lớp proxy để lưu trữ thực thể của bạn hoặc ít nhất là trả về một đối tượng ẩn danh. Theo quan điểm của tôi, việc sử dụng ef yêu cầu tạo các lớp proxy để triển khai logic của bạn, hãy sử dụng edmx giống như lớp truy cập db không phải là công việc kinh doanh.
Gonzix

vâng trong khung nhìn lưới, tôi cũng nhận được một cột bảng khác. LoanProviderName là gì.
baran

1
Hãy thử db.MemberLoans.Include("LoanProduct").OrderByDescending()kiểm tra cú pháp vì tôi không có VS trước mặt.
Nilesh

3
Bạn chỉ cần tiếp tục bao gồm tất cả các thuộc tính điều hướng mà bạn đang truy cập bên ngoài ngữ cảnh như thế nào db.MemberLoans.Include("LoanProduct").Include("SomeOtherTable). Đánh dấu vào câu trả lời bằng @Tragedian và @lazyberezovsky
Nilesh

Câu trả lời:


175

Theo mặc định Entity Framework sử dụng tính năng tải chậm cho các thuộc tính điều hướng. Đó là lý do tại sao các thuộc tính này nên được đánh dấu là ảo - EF tạo lớp proxy cho thực thể của bạn và ghi đè các thuộc tính điều hướng để cho phép tải chậm. Ví dụ: nếu bạn có thực thể này:

public class MemberLoan
{
   public string LoandProviderCode { get; set; }
   public virtual Membership Membership { get; set; }
}

Entity Framework sẽ trả lại proxy được kế thừa từ thực thể này và cung cấp phiên bản DbContext cho proxy này để cho phép tải tư cách thành viên một cách chậm rãi sau này:

public class MemberLoanProxy : MemberLoan
{
    private CosisEntities db;
    private int membershipId;
    private Membership membership;

    public override Membership Membership 
    { 
       get 
       {
          if (membership == null)
              membership = db.Memberships.Find(membershipId);
          return membership;
       }
       set { membership = value; }
    }
}

Vì vậy, entity có thể hiện của DbContext được sử dụng để tải entity. Đó là vấn đề của bạn. Bạn có usingkhối xung quanh việc sử dụng CosisEntities. Điều này xử lý ngữ cảnh trước khi các thực thể được trả lại. Khi một số mã sau đó cố gắng sử dụng thuộc tính điều hướng được tải lười biếng, nó không thành công vì ngữ cảnh được xử lý tại thời điểm đó.

Để khắc phục hành vi này, bạn có thể sử dụng tải nhanh các thuộc tính điều hướng mà bạn sẽ cần sau này:

IQueryable<MemberLoan> query = db.MemberLoans.Include(m => m.Membership);

Điều đó sẽ tải trước tất cả tư cách thành viên và tính năng tải chậm sẽ không được sử dụng. Để biết chi tiết, hãy xem bài viết Tải các Đối tượng Liên quan trên MSDN.


Cảm ơn rất nhiều vì lời giải thích và câu trả lời hữu ích của bạn. Trên thực tế ở đây tôi đang bao gồm ba bảng vì vậy tôi không biết làm thế nào tôi có thể thêm ba bảng với INCLUDE. bạn có thể vui lòng giúp tôi về điều này xin vui lòng.
baran

8
@barsan chỉ bao gồm tất cả các thuộc tính điều hướng từng cái một. Ví dụ: db.MemberLoans.Include(m => m.Membership).Include(m => m.LoanProduct).OrderByDescending(m => m.LoanDate);điều đó sẽ tạo truy vấn JOIN và trả về tất cả dữ liệu cùng một lúc.
Sergey Berezovskiy

1
Cảm ơn rất nhiều lazyberezovsky. Tôi rất biết ơn bạn. Bạn đã cứu tôi gần một ngày. Từ lời giải thích của bạn, tôi đang tìm hiểu thêm về Entity Framework. Cám ơn bạn tôi.
baran

Cảm ơn người bạn đời, hoàn hảo. Tôi đã có một câu lệnh sử dụng đang hạn chế tải chậm. Câu trả lời chính xác.
ncbl

4
Điều gì xảy ra nếu tôi không muốn đưa các thực thể có liên quan đó vào truy vấn của mình?
Ortund

32

Các CosisEntitieslớp học là của bạn DbContext. Khi bạn tạo ngữ cảnh trong một usingkhối, bạn đang xác định ranh giới cho hoạt động hướng dữ liệu của mình.

Trong mã của bạn, bạn đang cố gắng đưa ra kết quả của một truy vấn từ một phương thức và sau đó kết thúc ngữ cảnh bên trong phương thức. Thao tác bạn chuyển kết quả đến sau đó cố gắng truy cập các thực thể để điền vào chế độ xem lưới. Ở đâu đó trong quá trình liên kết với lưới, một thuộc tính được tải chậm đang được truy cập và Entity Framework đang cố gắng thực hiện tra cứu để lấy các giá trị. Nó không thành công, bởi vì ngữ cảnh liên quan đã kết thúc.

Bạn có hai vấn đề:

  1. Bạn đang tải các thực thể lười biếng khi bạn liên kết với lưới. Điều này có nghĩa là bạn đang thực hiện nhiều thao tác truy vấn riêng biệt với SQL Server, điều này sẽ làm mọi thứ chậm lại. Bạn có thể khắc phục sự cố này bằng cách đặt các thuộc tính liên quan được tải sẵn theo mặc định hoặc yêu cầu Entity Framework đưa chúng vào kết quả của truy vấn này bằng cách sử dụngInclude phương thức mở rộng.

  2. Bạn đang kết thúc ngữ cảnh của mình quá sớm: a DbContextnên có sẵn trong toàn bộ đơn vị công việc đang được thực hiện, chỉ xử lý nó khi bạn đã hoàn thành công việc trong tầm tay. Trong trường hợp ASP.NET, một đơn vị công việc thường là yêu cầu HTTP đang được xử lý.


Cảm ơn bạn rất nhiều vì thông tin hữu ích và giải thích tốt về vấn đề. Trên thực tế, tôi rất mới trong Entity Framework cũng như Linq nên thông tin này thực sự là một bài học tuyệt vời để tôi học hỏi.
baran

20

Kết luận

Mã của bạn đã truy xuất dữ liệu (thực thể) thông qua khung thực thể với tính năng tải chậm và sau khi DbContext được xử lý, mã của bạn đang tham chiếu đến các thuộc tính (thực thể liên quan / quan hệ / điều hướng) không được yêu cầu rõ ràng.

Cụ thể hơn

Các InvalidOperationException với thông điệp này luôn luôn có nghĩa là cùng một điều: bạn đang yêu cầu dữ liệu (thực thể) từ thực thể khuôn khổ sau khi DbContext đã được xử lý.

Một trường hợp đơn giản:

(các lớp này sẽ được sử dụng cho tất cả các ví dụ trong câu trả lời này và giả sử tất cả các thuộc tính điều hướng đã được định cấu hình chính xác và có các bảng được liên kết trong cơ sở dữ liệu)

public class Person
{
  public int Id { get; set; }
  public string name { get; set; }
  public int? PetId { get; set; }
  public Pet Pet { get; set; }
}

public class Pet 
{
  public string name { get; set; }
}

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
}

Console.WriteLine(person.Pet.Name);

Dòng cuối cùng sẽ hiển thị InvalidOperationExceptionvì dbContext chưa tắt tính năng tải chậm và mã đang truy cập thuộc tính điều hướng Pet sau khi Ngữ cảnh đã được xử lý bởi câu lệnh using.

Gỡ lỗi

Làm thế nào để bạn tìm thấy nguồn gốc của ngoại lệ này? Ngoài việc xem xét bản thân ngoại lệ, sẽ được ném chính xác vào vị trí mà nó xảy ra, các quy tắc chung về gỡ lỗi trong Visual Studio áp dụng: đặt các điểm ngắt chiến lược và kiểm tra các biến của bạn , bằng cách di chuột qua tên của chúng, mở ( Nhanh) Xem cửa sổ hoặc sử dụng các bảng gỡ lỗi khác nhau như Người dân địa phương và Ô tô.

Nếu bạn muốn tìm hiểu nơi đặt hoặc chưa đặt tham chiếu, hãy nhấp chuột phải vào tên của tham chiếu và chọn "Tìm tất cả tham chiếu". Sau đó, bạn có thể đặt một điểm ngắt ở mọi vị trí yêu cầu dữ liệu và chạy chương trình của bạn với trình gỡ lỗi được đính kèm. Mỗi khi trình gỡ lỗi xảy ra trên một điểm ngắt như vậy, bạn cần xác định xem thuộc tính điều hướng của mình có nên được điền hay không hay dữ liệu được yêu cầu là cần thiết.

Cách để Tránh

Tắt tính năng tải bằng cách lười biếng

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

Ưu điểm: Thay vì ném ra InvalidOperationException, thuộc tính sẽ là null. Việc truy cập các thuộc tính của null hoặc cố gắng thay đổi các thuộc tính của thuộc tính này sẽ ném ra một NullReferenceException .

Cách yêu cầu đối tượng một cách rõ ràng khi cần:

using (var db = new dbContext())
{
  var person = db.Persons
    .Include(p => p.Pet)
    .FirstOrDefaultAsync(p => p.id == 1);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Trong ví dụ trước, Khung thực thể sẽ hiện thực hóa Vật nuôi ngoài Con người. Điều này có thể có lợi vì đó là một lệnh gọi cơ sở dữ liệu duy nhất. (Tuy nhiên, cũng có thể có các vấn đề về hiệu suất lớn tùy thuộc vào số lượng kết quả trả về và số lượng thuộc tính điều hướng được yêu cầu, trong trường hợp này, sẽ không bị phạt về hiệu suất vì cả hai trường hợp chỉ là một bản ghi duy nhất và một phép nối duy nhất).

hoặc là

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);

  var pet = db.Pets.FirstOrDefaultAsync(p => p.id == person.PetId);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Trong ví dụ trước, Entity Framework sẽ hiện thực hóa Pet độc lập với Person bằng cách thực hiện một lệnh gọi bổ sung đến cơ sở dữ liệu. Theo mặc định, Entity Framework theo dõi các đối tượng mà nó đã truy xuất từ ​​cơ sở dữ liệu và nếu nó tìm thấy các thuộc tính điều hướng phù hợp, nó sẽ tự động điền các thực thể này một cách kỳ diệu . Trong trường hợp này bởi vì PetIdtrên Personđối tượng phù hợp với Pet.Id, Entity Framework sẽ gán Person.Petcho Petgiá trị lấy ra, trước khi giá trị được gán cho biến con vật cưng.

Tôi luôn khuyến nghị cách tiếp cận này vì nó buộc các lập trình viên phải hiểu khi nào và làm thế nào mã được yêu cầu dữ liệu thông qua Entity Framework. Khi mã ném một ngoại lệ tham chiếu rỗng trên một thuộc tính của một thực thể, bạn hầu như luôn có thể chắc chắn rằng bạn đã không yêu cầu dữ liệu đó một cách rõ ràng.


13

Đó là một câu trả lời rất muộn nhưng tôi đã giải quyết được sự cố khi tắt tải chậm:

db.Configuration.LazyLoadingEnabled = false;

Đối với tôi, StackOverflow hoạt động kỳ diệu với một lớp lót. Và điều này đã làm được điều đó cho tôi, chúc mừng bạn!
Harold_Finch 14/08/18

Nhược điểm là bạn phải sử dụng .Include và những thứ tương tự để tải các thuộc tính điều hướng.
boylec1986

1

Trong trường hợp của tôi, tôi đã dán tất cả các mô hình 'Người dùng' vào cột và nó không được ánh xạ chính xác, vì vậy tôi chỉ chuyển 'Tên người dùng' và nó đã sửa nó.

var data = db.ApplicationTranceLogs 
             .Include(q=>q.Users)
             .Include(q => q.LookupItems) 
             .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users,*** ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) 
             .ToList();

var data = db.ApplicationTranceLogs 
             .Include(q=>q.Users).Include(q => q.LookupItems) 
             .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users.Name***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) 
             .ToList();

1

Hầu hết các câu trả lời khác đều hướng đến việc tải nhanh, nhưng tôi đã tìm thấy một giải pháp khác.

Trong trường hợp của tôi, tôi có một đối tượng EF InventoryItemvới một tập hợp các InvActivityđối tượng con.

class InventoryItem {
...
   // EF code first declaration of a cross table relationship
   public virtual List<InvActivity> ItemsActivity { get; set; }

   public GetLatestActivity()
   {
       return ItemActivity?.OrderByDescending(x => x.DateEntered).SingleOrDefault();
   }
...
}

Và vì tôi đang kéo từ bộ sưu tập đối tượng con thay vì truy vấn ngữ cảnh (với IQueryable), nên Include()hàm không khả dụng để triển khai tải nhanh. Vì vậy, thay vào đó, giải pháp của tôi là tạo bối cảnh từ nơi tôi đã sử dụng GetLatestActivity()attach()đối tượng được trả về:

using (DBContext ctx = new DBContext())
{
    var latestAct = _item.GetLatestActivity();

    // attach the Entity object back to a usable database context
    ctx.InventoryActivity.Attach(latestAct);

    // your code that would make use of the latestAct's lazy loading
    // ie   latestAct.lazyLoadedChild.name = "foo";
}

Vì vậy, bạn không bị mắc kẹt với tải háo hức.


Về cơ bản, đây là quá trình tải mong muốn, bạn đã tải đối tượng qua một ngữ cảnh. Chỉ có hai lựa chọn; tải háo hức và tải chậm.
Erik Philips

@ErikPhilips right, đó là tải chậm với bối cảnh dữ liệu mới
Zorgarath

1
@ErikPhilips - cũng có Đang tải rõ ràng - docs.microsoft.com/en-us/ef/ef6/querying/…
Dave Black

1

Nếu bạn đang sử dụng ASP.NET Core và tự hỏi tại sao bạn nhận được thông báo này trong một trong các phương pháp điều khiển không đồng bộ của mình, hãy đảm bảo rằng bạn trả về một Taskthay vì void- ASP.NET Core loại bỏ các ngữ cảnh được chèn vào.

(Tôi đăng câu trả lời này vì câu hỏi này chiếm tỷ lệ cao trong kết quả tìm kiếm cho thông báo ngoại lệ đó và đó là một vấn đề nhỏ - có thể nó hữu ích cho những người sử dụng Google cho câu hỏi đó.)

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.