22

ASP.NETMVCアプリケーションでのEntityFrameworkコンテキストの望ましい有効期間についていくつか質問があります。コンテキストを可能な限り短い時間存続させるのが最善ではありませんか?

次のコントローラーアクションを検討してください。

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = context.MyTable;
    }

    return View(model);
}

ビューがページをレンダリングしているときにEntityFrameworkコンテキストがスコープ外になっているため、上記のコードは機能しません。他の人は上記のコードをどのように構成しますか?

4

4 に答える 4

52

物議を醸しましょう!

リクエスト全体を通してコンテキストを維持することが良いことであるという一般的な MVC + EF コンセンサスには、いくつかの理由で同意しません。

パフォーマンスの向上が 少ない 新しいデータベース コンテキストを作成するのにどれだけコストがかかるか知っていますか? うーん... " DataContextは軽量で、作成するのに費用がかかりません"それはMSDNからのものです

IoC を間違えると問題ないように見えます..実際に 使用するまで IoC コンテナーを設定してコンテキストを破棄する場合、それが間違っていると、本当に間違っていることになります。IoC コンテナーから作成された大規模なメモリ リークが、コンテキストを常に正しく処理しているとは限らないことを2 回確認しました。サーバーが通常のレベルの同時ユーザーの間に崩壊し始めるまで、設定が間違っていることに気付くことはありません。開発中は発生しないため、負荷テストを行ってください。

偶発的な遅延読み込み 最新の記事の IQueryable を返して、ホームページにリストできるようにします。ある日、別の誰かが、それぞれの記事の横にコメント数を表示するよう求められました。そのため、ビューに簡単なコードを追加して、コメント数を次のように表示します...

@foreach(var article in Model.Articles) {
    <div>
        <b>@article.Title</b> <span>@article.Comments.Count() comments</span>
    </div>
}

見た目も良く、動作も良好です。しかし実際には、返されたデータにコメントが含まれていなかったため、ループ内の記事ごとに新しいデータベース呼び出しが行われます。SELECT N+1 発行。10 件の記事 = 11 件のデータベース呼び出し。さて、コードは間違っていますが、それは起こりやすい間違いです。

データ層でコンテキストをシャットダウンすることで、これを防ぐことができます。しかし、 article.Comments.Count() の NullReferenceException でコードが壊れることはありませんか? はい、そのため、データレイヤーを編集して、ビュー レイヤーに必要なデータを取得する必要があります。これが本来あるべき姿です。

コードのにおい ビューからデータベースにアクセスするのは何か問題があります。IQueryable が実際にはデータベースにヒットしていないことはわかっているので、そのオブジェクトは忘れてください。データレイヤーを離れる前に、データベースがヒットしていることを確認してください。

それで答えは

あなたのコードは(私の意見では)このようにする必要があります

データ層:

public List<Article> GetArticles()
{
    List<Article> model;

    using (var context = new MyEntities())
    {
        //for an example I've assumed your "MyTable" is a table of news articles
        model = (from mt in context.Articles
                select mt).ToList();
        //data in a List<T> so the database has been hit now and data is final
    }

    return model;
}

コントローラ:

public ActionResult Index()
{
    var model = new HomeViewModel(); //class with the bits needed for you view
    model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised
    return View(model);
}

これを実行してこれを理解したら、おそらく IoC コンテナーでコンテキストを処理する実験を開始できますが、それ以前ではありません。私の警告に頭を上げてください-私は2つの大規模な失敗を見ました:)

しかし、正直なところ、好きなことをしてください。プログラミングは楽しいものであり、好みの問題であるべきです。私はあなたに私のことを言っているだけです。ただし、何をするにしても、「クールな子供たちがやっている」という理由だけで、コントローラーごとまたはリクエストごとに IoC コンテキストの使用を開始しないでください。その利点を本当に気にかけ、それがどのように正しく行われるかを理解しているからです。

于 2012-05-28T10:06:24.267 に答える
6

リクエストごとに 1 つのコンテキストに同意します。通常は、Ninject を使用してコンテキスト .InRequestScope をバインドすることでこれを行います。これは非常にうまく機能します。

Bind<MyContext>().ToSelf().InRequestScope();

また、クエリにできるだけ近いセットを列挙することも非常に良い方法です。

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = (from mt in context.MyTable
                select mt).ToArray();
    }
    return View(model);
}

これにより、ビューから意図せずにクエリが拡張されるのを防ぐことができます。

于 2012-05-27T22:27:03.767 に答える
2

まず、データベースへのアクセスを別のクラスに配置することを検討する必要があります。

次に、これに対する私のお気に入りの解決策は、「リクエストごとに 1 つのコンテキスト」を使用することです (MVC を使用している場合は、コントローラーごとに 1 つのコンテキストだと思います)。

リクエストされた編集:

この回答を見てください。おそらくあなたにも役立つでしょう。私はウェブフォームを使用しているため、現時点では MVC で確認できないことに注意してください。https://stackoverflow.com/a/10153406/1289283

この dbcontext の使用例:

public class SomeDataAccessClass
{
    public static IQueryable<Product> GetAllProducts()
    {
        var products = from o in ContextPerRequest.Current.Products
                       select o;
        return products;
    }
}

次に、次のようなことができます。

public ActionResult Index()
{
     var products = SomeDataAccessClass.GetProducts();
     return View(products);
}

シンプルですね。コンテキストを破棄することを心配する必要はもうありません。本当に必要なコードだけを記述します。

UnitOfWork パターンや IoC コンテナーを追加して、さらにスパイスを加えるのが好きな人もいますが、私はそのシンプルさからこのアプローチが好きです。

于 2012-05-27T22:22:11.830 に答える
1

.ToList()LINQの拡張メソッドをそのまま利用できますか:

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = (from mt in context.MyTable
                select mt).ToList();
    }
    return View(model);
}
于 2012-05-27T22:27:23.007 に答える