40

私はEFを初めて使用し、データベース型Userから情報クラスに変換する拡張メソッドを使用しようとしていますUserInfo
それが違いを生む場合、私は最初にデータベースを使用していますか?

以下の私のコードはエラーを出します

DbContext が破棄されているため、操作を完了できません。

try
{
    IQueryable<User> users;
    using (var dataContext = new dataContext())
    {
        users = dataContext.Users
                  .Where(x => x.AccountID == accountId && x.IsAdmin == false);
        if(users.Any() == false)
        {
            return null;
        }
    }
    return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
}
catch (Exception ex)
{
    //...
}

usersなぜそうなるのかはわかりますが、where ステートメントの結果がオブジェクトに保存されない理由もわかりません。

私の主な質問は、なぜそれが機能しないのか、そして次に拡張メソッドと EF を使用する正しい方法は何だと思いますか?

4

8 に答える 8

39

この質問と回答により、 IQueryable にはその操作にアクティブなコンテキストが必要であると私は信じるようになりました。つまり、代わりにこれを試す必要があります。

try
{
    IQueryable<User> users;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any() == false)
        {
            return null;
        }
        else
        {
            return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
        }
    }


}
catch (Exception ex)
{
    ...
}
于 2012-11-29T02:13:34.423 に答える
28

IQueryable<T>として公開され、 として公開されたオブジェクトIEnumerable<T>は、反復処理されるか、List<T>. EF が を返したIQueryable<T>場合、基本的にはデータを取得できるものを構成しているだけであり、実際にはそれを消費するまで取得を実行していません。

IQueryableが定義されている場所.ToList()と が呼び出されている場所にブレークポイントを設定することで、この感覚をつかむことができます。(Jofry が正しく指摘したように、データ コンテキストの範囲内から。) データをプルする作業は、ToList()呼び出し中に行われます。

IQueryable<T>そのため、データ コンテキストのスコープ内に保持する必要があります。

于 2012-11-29T02:26:04.260 に答える
16

IQueryableクエリは、列挙するまでデータストアに対して実際には実行されないことを覚えておく必要があります。

using (var dataContext = new dataContext())
{

このコード行は、実際にはSQLステートメントを作成する以外のことは何もしません。

    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

.Any()はIQueryableを列挙する操作であるため、SQLは(dataContextを介して)データソースに送信され、次に.Any()操作が実行されます。

    if(users.Any() == false)
    {
        return null;
    }
}

「問題」の行は、上記で作成したSQLを再利用してから、クエリに追加する追加の操作(.Select())を実行しています。ここに残しておけば、問題のある行を除いて例外はありません

return users.Select(x => x.ToInfo()).ToList(); // this line is the problem

.ToList()を呼び出します。これにより、IQueryableが列挙され、元のLINQクエリで使用されたdataContextを介してSQLがデータソースに送信されます。このdataContextは破棄されたため、無効になり、.ToList()は例外をスローします。

それが「うまくいかない理由」です。修正は、このコード行をdataContextのスコープ内に移動することです。

それを適切に使用する方法は、アプリケーション(フォーム、ASP.net、MVCなど)に応じて、ほぼ間違いなく正しい答えがいくつかある別の質問です。これが実装するパターンは、作業単位パターンです。新しいコンテキストオブジェクトを作成するのにほとんど費用がかからないため、一般的なルールは、オブジェクトを作成し、作業を行ってから破棄することです。Webアプリでは、リクエストごとにコンテキストを作成する人もいます。

于 2012-11-29T02:40:26.440 に答える
4

エラーがスローされる理由は、オブジェクトが破棄され、その後、オブジェクトを介してテーブル値にアクセスしようとしていますが、オブジェクトが破棄されているためです。値を取得できるように、それを ToList() に変換することをお勧めします

使用するまで実際にデータを取得していない可能性があります (遅延読み込みです)。そのため、作業を行おうとしているときに dataContext は存在​​しません。スコープ内で ToList() を実行した場合は、問題ないでしょう。

try
{
    IQueryable<User> users;
    var ret = null;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any())
        {
            ret = users.Select(x => x.ToInfo()).ToList(); 
        }

     }

   Return ret;
}
catch (Exception ex)
{
    ...
}
于 2012-11-29T02:23:04.890 に答える
2

これを変える:

using (var dataContext = new dataContext())
{
    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

    if(users.Any())
    {
        ret = users.Select(x => x.ToInfo()).ToList(); 
    }

 }

これに:

using (var dataContext = new dataContext())
{
    return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList();
} 

要点は、コンテキスト データセットの列挙を一度だけ強制したいということです。必要に応じて、呼び出し元に空のセットのシナリオを処理させます。

于 2016-04-25T10:23:11.440 に答える
2

これは、リポジトリに ToList() を追加するのと同じくらい簡単です。例えば:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        // causes an error
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id);
    }
}

呼び出し元のクラスで Db コンテキストによって破棄されたエラーが発生しますが、これは、LINQ 操作に ToList() を追加して明示的に列挙を実行することで解決できます。

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
    }
}
于 2014-11-18T16:07:28.643 に答える