none
entity frameworkのDbContextの閉じるタイミング RRS feed

  • 質問

  • いつもお世話になっております。

    ASP.NET MVC(ver4)でEntity Framework(ver5)を使って開発しています。

    操作で不明な点がありまして、質問させて頂きます。

    ○不明点

    コントローラー側では以下のようにusginで囲んで

    処理が終わったら開放するようにしています。

    using (var context = new TestEntities()) {

    ーーー処理ーーーー

    }

    上記コードが問題になる部分があります。

    それがview側で

    @foreach (var item in Model)

    {    

    ーーー処理ーーー

    }

    と行うと「操作を完了できません。DbContext は破棄されています。」とエラーが表示されます。

    View側でもModelを使う場合はどこでDbContexを破棄すれば良いでしょうか?

    まだ基礎が理解出来ていないようです。

    お手数おかけしますが、ご教授頂きたくお願い致します。

    2014年7月15日 7:11

回答

  • LINQ で重要な概念に遅延実行というのがあるんですが、そのあたりがきちんと理解できていないように思えます。

    > Entity FrameworkをLinqでselectした結果が「IQueryable」なので、そのデータをそのまま使っています。

    IQueryable というのは結果のデータではなく、データを取得するための「式」を保持している状態です。
    ここから実際のデータにさわろうとしたタイミングでこの式が評価され、DBへのSQL文の発行やデータの取得が行われます。
    これが遅延実行といわれる動きです。

    今だとModel中では「式」の状態で持っているだけなので、Viewのなかでデータに触ろうとしたときにSQL文を発行しようとしてエラーになっている、という状態におちいってしまっているのだと思います。

    前にも書いてますが、データの取得はViewに渡す前におこなってください。
    IQueryableで持っている式に .ToList() を追加すると、これはデータを取得してListの形式で保持することになるので、とりあえず今のエラーは回避できるようになりそうな気がします。
    まぁ、これだけで十分かというと疑問ではありますが。


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    • 回答としてマーク GacktFun 2014年7月16日 5:23
    2014年7月16日 4:09

すべての返信

  • Viewの中でDBを操作するような処理を記述していませんか?
    それが問題だと思います。

    Viewはあくまで表示する画面を生成する部分で、データの更新や取得といった処理はViewに渡すまえに終えておく必要があります。
    Viewに渡すModelは表示するデータを詰め込んだもの、まぁ、ViewModelだと考えるのがいいと思います。


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    2014年7月15日 10:10
  • 小野様

    ご返信ありがとうございます。

    modelに使っている型は「IQueryable」を使っています。これが問題でしょうか?

    Entity FrameworkをLinqでselectした結果が「IQueryable」なので、

    そのデータをそのまま使っています。


    2014年7月16日 1:45
  • LINQ で重要な概念に遅延実行というのがあるんですが、そのあたりがきちんと理解できていないように思えます。

    > Entity FrameworkをLinqでselectした結果が「IQueryable」なので、そのデータをそのまま使っています。

    IQueryable というのは結果のデータではなく、データを取得するための「式」を保持している状態です。
    ここから実際のデータにさわろうとしたタイミングでこの式が評価され、DBへのSQL文の発行やデータの取得が行われます。
    これが遅延実行といわれる動きです。

    今だとModel中では「式」の状態で持っているだけなので、Viewのなかでデータに触ろうとしたときにSQL文を発行しようとしてエラーになっている、という状態におちいってしまっているのだと思います。

    前にも書いてますが、データの取得はViewに渡す前におこなってください。
    IQueryableで持っている式に .ToList() を追加すると、これはデータを取得してListの形式で保持することになるので、とりあえず今のエラーは回避できるようになりそうな気がします。
    まぁ、これだけで十分かというと疑問ではありますが。


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    • 回答としてマーク GacktFun 2014年7月16日 5:23
    2014年7月16日 4:09
  • どういうコードでやっているのか書いてないので想像ですが、以下のページの AddressController.cs のコードで var db = ... から return View(list); までを using ブロックで囲んだところ、Index.aspx の foreach で例外がスローされるというようなことでしょうか?

    10 行でズバリ!! ASP.NET MVC を構成する各コンポーネントとネーミング ルール (C#)
    http://code.msdn.microsoft.com/10-ASPNET-MVC-C-8566eeb5/

    そうだとすると、foreach のところでエンティティデータオブジェクト経由 DB に接続してデータを取りに行くものの、その前に using ブロックを抜けたところで Dispose されていて View では使用できなくなっているので、例外がスローされると言うことだと思います。

    なので、上記ページのサンプルコードのような形にしている限りは(即ち、View でエンティティデータオブジェクトを使用する限りは)、Controller でエンティティデータオブジェクトを Dispose することはできません。

    View で Dispose できないこともないですが、そんなことはしなくても、ガベージコレクタに任せておけば良いと思います。

    #質問する時は、具体的にどういう手順でやっているのか書いてください。例えば、上に書いたように、Web にあるチュートリアルなどのサンプルコードを例に取るなどして。そうしないと、掲示板に書いてある情報が全ての回答者にとっては、質問者さんの状況は理解し難いです。

    2014年7月16日 5:20
  • 小野様

    ありがとうございます。

    遅延実行勉強してみます。


    2014年7月16日 5:24