[C#] “ObjectContext 인스턴스가 삭제되었으며 더 이상 연결이 필요한 작업에 사용할 수 없습니다.”InvalidOperationException 해결

GridViewEntity Frameworkm을 사용하여 채우려 고 하지만 매번 다음 오류가 발생합니다.

” ‘COSIS_DAL.MemberLoan’개체의 속성 접근 자 ‘LoanProduct’에서 다음 예외가 발생했습니다. ObjectContext 인스턴스가 삭제되었으며 더 이상 연결이 필요한 작업에 사용할 수 없습니다.”

내 코드는 다음과 같습니다.

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

오류가 언급된다 LoanProductName의 열 Gridview. 언급 : C #, ASP.net, SQL-Server 2008을 백엔드 DB로 사용하고 있습니다.

저는 Entity Framework를 처음 사용합니다. 이 오류가 발생하는 이유를 이해할 수 없습니다. 누구든지 제발 도와 줄 수 있습니까?



답변

기본적으로 Entity Framework는 탐색 속성에 지연로드를 사용합니다. 그렇기 때문에 이러한 속성을 가상으로 표시해야합니다. EF는 엔터티에 대한 프록시 클래스를 만들고 지연로드를 허용하도록 탐색 속성을 재정의합니다. 예 :이 엔티티가있는 경우 :

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

Entity Framework는 나중에 구성원의 지연로드를 허용하기 위해이 엔터티에서 상속 된 프록시를 반환하고이 프록시에 DbContext 인스턴스를 제공합니다.

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

따라서 엔터티에는 엔터티를로드하는 데 사용 된 DbContext의 인스턴스가 있습니다. 그게 당신 문제입니다. usingCosisEntities 사용 을 차단했습니다. 엔티티가 반환되기 전에 컨텍스트를 삭제합니다. 나중에 일부 코드가 지연로드 된 탐색 속성을 사용하려고하면 컨텍스트가 해당 순간에 삭제되기 때문에 실패합니다.

이 동작을 수정하려면 나중에 필요한 탐색 속성을 즉시로드 할 수 있습니다.

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

모든 멤버십을 미리로드하고 지연로드를 사용하지 않습니다. 자세한 내용은 MSDN의 관련 엔터티로드 문서 를 참조하십시오 .


답변

CosisEntities클래스는이다 DbContext. using블록 에서 컨텍스트를 생성 할 때 데이터 지향 작업의 경계를 정의합니다.

코드에서 메서드에서 쿼리 결과를 내 보낸 다음 메서드 내에서 컨텍스트를 종료하려고합니다. 결과를 전달하는 작업은 그리드보기를 채우기 위해 엔티티에 액세스를 시도합니다. 그리드에 바인딩하는 과정에서 지연로드 된 속성에 액세스하고 Entity Framework는 값을 얻기 위해 조회를 수행하려고합니다. 연관된 컨텍스트가 이미 종료 되었기 때문에 실패합니다.

두 가지 문제가 있습니다.

  1. 그리드에 바인딩 할 때 엔터티를 지연로드합니다. 이것은 SQL Server에 대해 많은 별도의 쿼리 작업을 수행하고 있음을 의미하므로 모든 것이 느려지 게됩니다. 관련 속성을 기본적으로 즉시로드하도록 만들거나 Include확장 메서드 를 사용하여이 쿼리의 결과에 포함하도록 Entity Framework에 요청하여이 문제를 해결할 수 있습니다 .

  2. 컨텍스트를 조기에 종료하고 있습니다. a DbContext는 수행중인 작업 단위 전체에서 사용할 수 있어야하며 현재 작업을 마쳤을 때만 폐기해야합니다. ASP.NET의 경우 작업 단위는 일반적으로 처리되는 HTTP 요청입니다.


답변

결론

지연 로딩이 활성화 된 엔터티 프레임 워크를 통해 코드가 데이터 (엔티티)를 검색했으며 DbContext가 삭제 된 후 코드는 명시 적으로 요청되지 않은 속성 (관련 / 관계 / 탐색 엔터티)을 참조합니다.

더 구체적으로

InvalidOperationException이 메시지는 항상 같은 일을 의미 다음 DbContext가 배치 된 후 엔티티 프레임 워크의 데이터 (실체)를 요청한다.

간단한 경우 :

(이 클래스는이 답변의 모든 예제에 사용되며 모든 탐색 속성이 올바르게 구성되고 데이터베이스에 연결된 테이블이 있다고 가정합니다)

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

마지막 줄은 InvalidOperationExceptiondbContext가 지연 로딩을 비활성화하지 않았고 Context가 using 문에 의해 삭제 된 후 코드가 Pet 탐색 속성에 액세스 하고 있기 때문에 를 throw합니다 .

디버깅

이 예외의 원인을 어떻게 찾습니까? 예외가 발생하는 위치에서 정확히 발생하는 예외 자체를 보는 것 외에도 Visual Studio에서 디버깅의 일반적인 규칙이 적용됩니다. 전략적 중단 점을 배치하고 변수를 검사합니다. 이름 위에 마우스를 올려 놓고 ( Quick) Watch window 또는 Locals 및 Autos와 같은 다양한 디버깅 패널 사용.

참조가 설정되어 있는지 여부를 찾으려면 해당 이름을 마우스 오른쪽 단추로 클릭하고 “모든 참조 찾기”를 선택하십시오. 그런 다음 데이터를 요청하는 모든 위치에 중단 점을 배치하고 연결된 디버거로 프로그램을 실행할 수 있습니다. 디버거가 이러한 중단 점에서 중단 될 때마다 탐색 속성을 채워야하는지 또는 요청 된 데이터가 필요한지 여부를 결정해야합니다.

피하는 방법

지연 로딩 비활성화

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

장점 : InvalidOperationException을 던지는 대신 속성은 null이됩니다. null 속성에 액세스하거나이 속성의 속성을 변경하려고하면 NullReferenceException이 발생 합니다.

필요할 때 객체를 명시 적으로 요청하는 방법 :

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

이전 예제에서 Entity Framework는 Person 외에 Pet을 구체화합니다. 이것은 데이터베이스에 대한 단일 호출이기 때문에 유리할 수 있습니다. (그러나 반환 된 결과 수와 요청 된 탐색 속성 수에 따라 큰 성능 문제가있을 수도 있습니다.이 경우 두 인스턴스가 모두 단일 레코드와 단일 조인이기 때문에 성능 저하가 없습니다).

또는

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

이전 예제에서 Entity Framework는 데이터베이스를 추가로 호출하여 Person과 독립적으로 Pet을 구체화합니다. 기본적으로 Entity Framework는 데이터베이스에서 검색 한 개체를 추적하고 일치하는 탐색 속성을 찾으면 이러한 엔터티를 자동으로 채 웁니다. 때문에이 경우 PetId상의 Person객체가 일치 Pet.Id, 엔티티 프레임 워크는를 할당 Person.Pet받는 Pet값이 애완 동물 변수에 할당되기 전에, 검색 값.

프로그래머가 코드가 Entity Framework를 통해 데이터를 요청하는시기와 방법을 이해하게하므로 항상이 방법을 권장합니다. 코드가 엔터티의 속성에 대해 null 참조 예외를 throw하면 거의 항상 해당 데이터를 명시 적으로 요청하지 않았는지 확인할 수 있습니다.


답변

매우 늦은 답변이지만 지연 로딩을 끄는 문제를 해결했습니다.

db.Configuration.LazyLoadingEnabled = false;


답변

제 경우에는 모든 모델 ‘Users’를 열에 전달했지만 올바르게 매핑되지 않았으므로 ‘Users.Name’을 전달하고 수정했습니다.

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


답변

다른 답변의 대부분은 eager loading을 가리 키지 만 다른 해결책을 찾았습니다.

제 경우 InventoryItem에는 InvActivity자식 개체 컬렉션 이있는 EF 개체 가 있습니다.

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

컨텍스트 쿼리 (사용 IQueryable) 대신 자식 개체 컬렉션에서 가져 왔기 때문에이 Include()함수는 즉시로드를 구현하는 데 사용할 수 없었습니다. 그래서 대신 내 해결책은 내가 활용 한 컨텍스트 GetLatestActivity()attach()반환 된 객체 를 만드는 것이 었습니다 .

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

따라서 당신은 eager loading에 갇혀 있지 않습니다.


답변

당신이 당신의 비동기 컨트롤러 방법 중 하나에이 메시지가 왜, ASP.NET 코어와 경이로움을 사용하는 경우 반드시 당신은을 반환 Task하는 대신 void– ASP.NET 코어 처분하는이 상황을 주입.

(이 질문은 예외 메시지에 대한 검색 결과에서 높고 미묘한 문제이기 때문에이 답변을 게시하고 있습니다. Google 사용자에게 유용 할 수 있습니다.)