Entity Frameworkについて長い間私を悩ませてきたことがあります。
昨年、EFを使用するクライアント向けに大きなアプリケーションを作成しました。そして開発中、すべてがうまくいきました。
8月にシステムを出荷しました。しかし、数週間後、本番サーバーで奇妙なメモリリークが発生し始めました。私のASP.NET MVC 4プロセスは、数日実行した後、マシンのすべてのリソースを消費していました(8 GB)。これは良くありませんでした。私はネットを検索しましたが、コンテキストを破棄できるように、すべてのEFクエリと操作をusing()
ブロックで囲む必要があることがわかりました。
ある日、私はすべてのコードをusing()
を使用するようにリファクタリングしました。これにより問題が解決しました。それ以来、プロセスは安定したメモリ使用量にとどまっています。
最初にクエリを囲まなかった理由は、Visual Studioに含まれているMicrosoft独自のスキャフォールドから最初のコントローラーとリポジトリを開始したためです。これらは、クエリを使用して囲みませんでした。代わりに、インスタンス変数としてDbContext
がありました。コントローラ自体の。
まず第一に:コンテキストを破棄することが本当に重要である場合(奇妙ではないもの、dbconnection
を閉じる必要があるなど)、Microsoftはすべての例でこれを使用する必要があります!
今、私はすべての学習を頭の後ろに置いて新しい大きなプロジェクトに取り組み始め、.NET 4.5とEF 6の新機能async
とawait
を試しています。 EF 6.0には、これらすべての非同期メソッドがあります(SaveChangesAsync
、ToListAsync
など)。
_public Task<tblLanguage> Post(tblLanguage language)
{
using (var langRepo = new TblLanguageRepository(new Entities()))
{
return langRepo.Add(RequestOrganizationTypeEnum, language);
}
}
_
クラスTblLanguageRepo
内:
_public async Task<tblLanguage> Add(OrganizationTypeEnum requestOrganizationTypeEnum, tblLanguage language)
{
...
await Context.SaveChangesAsync();
return langaugeDb;
}
_
ただし、ステートメントをusing()
ブロックで囲むと、クエリが戻る前に、例外_DbContext was disposed
_が発生します。これは予想される動作です。クエリは非同期で実行され、using
ブロックはクエリの前に完了します。しかし、ef 6のasync関数とawait関数を使用しているときに、コンテキストを適切に破棄するにはどうすればよいですか?
正しい方向に向けてください。
using()
はEF 6で必要ですか?マイクロソフト独自の例がなぜこれを取り上げないのですか?非同期機能をどのように使用し、コンテキストを適切に破棄しますか?
あなたのコード:
public Task<tblLanguage> Post(tblLanguage language)
{
using (var langRepo = new TblLanguageRepository(new Entities()))
{
return langRepo.Add(RequestOrganizationTypeEnum, language);
}
}
は、Task
を返す前にリポジトリを破棄しています。コードを作成した場合async
:
public async Task<tblLanguage> Post(tblLanguage language)
{
using (var langRepo = new TblLanguageRepository(new Entities()))
{
return await langRepo.Add(RequestOrganizationTypeEnum, language);
}
}
次に、Task
が完了する直前にリポジトリを破棄します。実際に発生するのは、await
を押すと、メソッドは不完全なTask
を返します(この時点では、using
ブロックはまだ「アクティブ」です)。次に、langRepo.Add
タスクが完了すると、Post
メソッドが実行を再開し、langRepo
を破棄します。 Post
メソッドが完了すると、返されたTask
が完了します。
詳細については、my async
intro を参照してください。
私は「リクエストごとに1つのDbContext」を選択し、リクエスト内でDbContextを再利用します。とにかく、すべてのタスクは要求の最後に完了する必要があるため、安全に再度処分できます。
次を参照してください: ASP.NET MVCのリクエストごとに1つのDbContext(IOCコンテナーなし)
他のいくつかの利点:
using
ステートメントがすべてあるわけではありません。I @ Dirk Boerと同意する DbContextの有効期間を管理する最善の方法は、httpリクエストが完了するとコンテキストを破棄するIoCコンテナを使用することです。ただし、それが選択肢でない場合は、次のようなこともできます。
var dbContext = new MyDbContext();
var results = await dbContext.Set<MyEntity>.ToArrayAsync();
dbContext.Dispose();
using
ステートメントは、コードブロックの最後にあるオブジェクトを破棄するための構文上の糖衣です。 .Dispose
を自分で呼び出すだけで、using
ブロックなしで同じ効果を得ることができます。
考えてみてください。usingブロック内でawait
キーワードを使用する場合、オブジェクト破棄例外を取得しないでください。
public async Task<tblLanguage> Post(tblLanguage language)
{
using (var langRepo = new TblLanguageRepository(new Entities()))
{
var returnValue = langRepo.Add(RequestOrganizationTypeEnum, language);
await langRepo.SaveChangesAsync();
return returnValue;
}
}
適切なn層のプログラミングパターンを使用している場合、コントローラーは、データベース要求が行われていることさえ知らないはずです。これはすべて、サービス層で発生するはずです。
これを行うにはいくつかの方法があります。 1つは、クラスごとに2つのコンストラクターを作成することです。1つはコンテキストを作成し、もう1つは既存のコンテキストを受け入れます。このようにして、すでにサービスレイヤーにいる場合はコンテキストを渡したり、サービスレイヤーを呼び出すコントローラー/モデルの場合は新しいコンテキストを作成したりできます。
もう1つは、各メソッドの内部オーバーロードを作成し、そこでコンテキストを受け入れることです。
しかし、はい、これらを使用中にラップする必要があります。
理論的には、ガベージコレクションはこれらをラップせずにクリーンアップする必要があります(SHOULD)が、GCを完全に信頼しているわけではありません。
メソッドの同期を維持したいが、非同期でDBに保存したい場合は、usingステートメントを使用しないでください。 @danludwigが言ったように、それは単なる構文上の砂糖です。 SaveChangesAsync()メソッドを呼び出し、タスクの完了後にコンテキストを破棄できます。これを行う1つの方法は次のとおりです。
//Save asynchronously then dispose the context after
context.SaveChangesAsync().ContinueWith(c => context.Dispose());
ContinueWith()に渡したラムダも非同期で実行されることに注意してください。
私見、これも遅延読み込みの使用が原因の問題です。コンテキストを破棄すると、データベースサーバーへの基本的な接続が閉じられるため、プロパティをレイジーロードできなくなります。
遅延読み込みを有効にしていて、using
スコープの後に例外が発生する場合は、 https:/を参照してください。 /stackoverflow.com/a/21406579/870604