Entity Frameworkmを使用してGridView
を設定しようとしていますが、次のエラーが発生するたびに:
「オブジェクト '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
列に言及しています。言及:バックエンドDBとしてC#、ASP.net、SQL-Server 2008を使用しています。
私は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のインスタンスがあります。それはあなたの問題だ。 CosisEntitiesの使用の周りにusing
ブロックがあります。エンティティが返される前にコンテキストを破棄します。後でコードが遅延ロードされたナビゲーションプロパティを使用しようとすると、その時点でコンテキストが破棄されるため失敗します。
この動作を修正するには、後で必要になるナビゲーションプロパティの積極的な読み込みを使用できます。
IQueryable<MemberLoan> query = db.MemberLoans.Include(m => m.Membership);
これにより、すべてのメンバーシップが事前にロードされ、遅延ロードは使用されません。詳細については、MSDNの Loading Related Entities の記事を参照してください。
CosisEntities
クラスはDbContext
です。 using
ブロックにコンテキストを作成すると、データ指向の操作の境界が定義されます。
コードでは、メソッドからクエリの結果を出力し、メソッド内のコンテキストを終了しようとしています。結果を渡す操作は、グリッドビューに入力するためにエンティティにアクセスしようとします。グリッドにバインドするプロセスのどこかで、遅延ロードされたプロパティにアクセスし、Entity Frameworkがルックアップを実行して値を取得しようとしています。関連付けられたコンテキストが既に終了しているため、失敗します。
次の2つの問題があります。
グリッドにバインドすると、エンティティを遅延読み込みします。これは、SQL Serverに対して多くの個別のクエリ操作を実行していることを意味し、すべてが遅くなります。この問題を解決するには、デフォルトで関連プロパティを積極的にロードするか、Entity Frameworkに Include
拡張メソッドを使用して、このクエリの結果にそれらを含めるように依頼します。
コンテキストを時期尚早に終了しています: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);
最後の行は、InvalidOperationException
をスローします。dbContextは遅延読み込みを無効にしておらず、コードはusingステートメントによってコンテキストが破棄された後にPetナビゲーションプロパティにアクセスしているためです。
この例外の原因をどのように見つけますか?例外が発生した場所で正確にスローされる例外自体を確認する以外に、Visual Studioでのデバッグの一般的なルールが適用されます:戦略的なブレークポイントを配置し、マウスをホバーすることで 変数を検査 名前の上に、(クイック)ウォッチウィンドウを開くか、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はデータベースから取得したオブジェクトを追跡し、それに一致するナビゲーションプロパティが見つかった場合、これらのエンティティにauto-magicallyを追加します。この場合、PetId
オブジェクトのPerson
がPet.Id
と一致するため、Entity Frameworkは、値がペット変数に割り当てられる前に、取得したPet
値にPerson.Pet
を割り当てます。
いつ、どのようにコードがEntity Frameworkを介してデータを要求するかをプログラマーに理解させるため、このアプローチを常にお勧めします。コードがエンティティのプロパティでnull参照例外をスローする場合、ほとんどの場合、そのデータを明示的に要求していないことを確認できます。
非常に遅い回答ですが、遅延読み込みをオフにする問題を解決しました:
db.Configuration.LazyLoadingEnabled = false;
ASP.NET Coreを使用していて、非同期コントローラーメソッドの1つでこのメッセージが表示される理由を知りたい場合は、Task
ではなくvoid
を返すようにしてください。ASP.NETCoreは注入されたコンテキストを破棄します。
(この質問はその例外メッセージに対する検索結果で高く、微妙な問題であるため、この回答を投稿しています-多分それはGoogleのために役立つでしょう。)
他の回答のほとんどは、積極的な読み込みを指しますが、別の解決策を見つけました。
私の場合、EFオブジェクトInventoryItem
と、InvActivity
子オブジェクトのコレクションがありました。
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";
}
したがって、熱心なロードにこだわることはありません。
私の場合、すべてのモデル「ユーザー」を列に渡していましたが、正しくマップされていなかったため、「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();