「ObjectContextインスタンスは破棄され、接続を必要とする操作には使用できなくなりました」を解決するInvalidOperationException


122

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はまったく新しいものです。このエラーが発生する理由を理解できません。誰かが私を助けてくれますか?


1
gridviewのナビゲーションプロパティにアクセスしていますか?その場合は、これらのナビゲーションテーブルもクエリに含める必要があります。いいねquery.Include("SomeOtherTable")
ニレシュ

エンティティをホストするプロキシクラスを作成するか、少なくとも匿名オブジェクトを返します。私の観点から見ると、efを使用するには、ロジックを実装するためのプロキシクラスを作成する必要があります。edmxは、ビジネスとしてではなく、dbアクセスレイヤーと同じように使用してください。
Gonzix 2013

はい、グリッドビューで別のテーブル列も取得しています。それはLoanProviderNameです。
baran 2013

1
試してみてくださいdb.MemberLoans.Include("LoanProduct").OrderByDescending()、私は私の前にVSを持っていけない構文的な原因を確認してください。
ニレシュ2013

3
のように、コンテキストの外でアクセスしているすべてのナビゲーションプロパティを含める必要がありますdb.MemberLoans.Include("LoanProduct").Include("SomeOtherTable)。@Tragedianと@lazyberezovskyによる回答をチェック
Nilesh

回答:


174

デフォルトでは、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の関連エンティティの読み込みに関する記事を参照してください。


役立つ説明と回答をありがとうございました。実際にはここに3つのテーブルを含めているため、INCLUDEを使用して3つのテーブルを追加する方法がわかりません。これについて教えてください。
baran 2013

8
@barsanは、すべてのナビゲーションプロパティを1つずつ含めるだけです。たとえばdb.MemberLoans.Include(m => m.Membership).Include(m => m.LoanProduct).OrderByDescending(m => m.LoanDate);、JOINクエリを生成し、すべてのデータを一度に返します。
セルゲイベレゾフスキー2013

1
たくさんlazyberezovskyに感謝します。私はあなたにとても感謝しています。あなたは私をほぼ一日救った。あなたの説明から、Entity Frameworkについてもっと学びます。友よありがとう。
baran 2013

ありがとう、メイト、パーフェクト。遅延読み込みを制限するusingステートメントがありました。すばらしい答えです。
ncbl 2014年

4
これらの関連エンティティをクエリにまったく含めない場合はどうなりますか?
Ortund、

32

CosisEntitiesクラスはあなたですDbContextusingブロックでコンテキストを作成すると、データ指向の操作の境界を定義することになります。

コードでは、メソッドからクエリの結果を出力し、メソッド内のコンテキストを終了しようとしています。結果を渡す操作は、エンティティにアクセスしてグリッドビューにデータを入力しようとします。グリッドにバインドするプロセスのどこかで、遅延読み込みされたプロパティにアクセスしていて、Entity Frameworkは値を取得するためにルックアップを実行しようとしています。関連するコンテキストがすでに終了しているため、失敗します。

次の2つの問題があります。

  1. グリッドにバインドするときにエンティティを遅延読み込みしています。これは、SQL Serverに対して多数の個別のクエリ操作を実行していることを意味し、すべての処理速度が低下します。この問題は、関連するプロパティをデフォルトでイーガーロードにするか、Entity Frameworkにこのクエリの結果に含めるように要求することで修正できます。Include拡張メソッド。

  2. コンテキストが途中で終了します。DbContext実行中の作業ユニット全体でを使用できるようにし、手元の作業が完了したときにのみそれを破棄します。ASP.NETの場合、作業単位は通常、処理されるHTTP要求です。


問題の有益な情報と良い説明をありがとうございました。実際、私はEntity FrameworkとLinqの両方で非常に新しいので、この情報は本当に学ぶべき素晴らしい教訓です。
baran 2013

20

ボトムライン

コードは、遅延読み込みが有効になっているエンティティフレームワークを介してデータ(エンティティ)を取得し、DbContextが破棄された後、コードは明示的に要求されなかったプロパティ(関連/関係/ナビゲーションエンティティ)を参照しています。

すなわち

InvalidOperationExceptionこのメッセージ withは常に同じことを意味します。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);

最後の行はInvalidOperationException、dbContextが遅延読み込みを無効にしておらず、コードがusingステートメントによってContextが破棄された後にPetナビゲーションプロパティにアクセスしているためにスローされます。

デバッグ中

この例外の原因をどのようにして見つけますか?例外が発生した場所で正確にスローされる例外自体を見ることとは別に、Visual Studioでのデバッグの一般的なルールが適用されます。戦略的なブレークポイントを配置し、変数の上にマウスを置くことによって変数検査します。 Quick)Watchウィンドウ、または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に加えてペットを具体化します。データベースを1回呼び出すだけなので、これは有利です。(ただし、返された結果の数と要求されたナビゲーションプロパティの数によっては、パフォーマンスに大きな問題が発生する可能性があります。この場合、両方のインスタンスが単一のレコードと単一の結合にすぎないため、パフォーマンスが低下することはありません)。

または

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とは関係なくペットを具体化します。デフォルトでは、Entity Frameworkはデータベースから取得したオブジェクトを追跡し、それに一致するナビゲーションプロパティが見つかると、これらのエンティティに自動的に値を設定します。このインスタンスではPetIdPersonオブジェクトのがと一致するためPet.Id、Entity Frameworkは取得Person.PetしたPet値にを割り当ててから、値がペット変数に割り当てられます。

いつでもどのようにコードがEntity Frameworkを介してデータを要求するかをプログラマーに理解させるため、私は常にこのアプローチをお勧めします。コードがエンティティのプロパティでnull参照例外をスローする場合、ほとんどの場合、そのデータを明示的に要求していないことを確認できます。


13

それは非常に遅い答えですが、私は遅延読み込みをオフにする問題を解決しました:

db.Configuration.LazyLoadingEnabled = false;

私にとって、StackOverflowはワンライナーで不思議に機能します。そして、これは私のために、あなたへの称賛をしました!
Harold_Finch

欠点は、.Includeなどを使用してナビゲーションプロパティを読み込む必要があることです。
boylec1986年

1

私の場合、すべてのモデル「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();

1

他のほとんどの答えは熱心な読み込みを指しますが、別の解決策を見つけました。

私の場合InventoryItemInvActivity子オブジェクトのコレクションを持つ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";
}

したがって、熱心なロードに悩まされることはありません。


これは基本的に熱心なロードであり、コンテキストを介してオブジェクトをロードしました。オプションは2つしかありません。熱心な読み込みと遅延読み込み。
エリックフィリップス

@ErikPhilips右、それは新しいデータコンテキストで遅延ロードだ
Zorgarath

1
@ErikPhilips -そこに明示的なロードでもある- docs.microsoft.com/en-us/ef/ef6/querying/...
デイブ・ブラック

1

ASP.NET Coreを使用していて、非同期コントローラーメソッドの1つでこのメッセージが表示される理由を不思議に思っている場合は、Taskではなく、voidASP.NET Coreが挿入されたコンテキストを破棄することを確認してください。

(この質問はその例外メッセージに対する検索結果の上位にあり、微妙な問題であるため、私はこの回答を投稿しています。多分それは、Googleでそれをする人にとって役立つかもしれません。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.