0

このコードが foreach ループの前に dbcontext を破棄する理由を理解しようとして苦労してきました。手がかりは大歓迎です。ありがとう!

    [TestMethod]
    public void CreatePostAddComment()
    {
        IQueryable<Post> thePost;
        using (var _context = new BlogRepository())
        {
            _context.AddPost(GetTestPost());
            var retrivePost = _context.GetPostByName("TestPost1");
            thePost = retrivePost;
        }

        foreach (var post in thePost)
        {
            using (var _dbContext = new BlogRepository())
            {
                _dbContext.AddComment(GetTestComment(post));
            }

        }
    }
4

1 に答える 1

2

using ステートメントは、実際には構文糖衣であり、次と同等です: -

[TestMethod]
public void CreatePostAddComment()
{
    IQueryable<Post> thePost;
    {
        var _context = new BlogRepository();
        _context.AddPost(GetTestPost());
        var retrivePost = _context.GetPostByName("TestPost1");
        thePost = retrivePost;
        _context.Dispose();
       //Note, using is much better, because Dipose is called
       //even if there is an exception.
    }

    foreach (var post in thePost)
    {
        using (var _dbContext = new BlogRepository())
        {
            _dbContext.AddComment(GetTestComment(post));
        }

    }
}

ただし、コードにはいくつかの重要な問題があり、その最初の 1 つだけが Context Disposed Exception です。

破棄されるコンテキストに関する最初の問題は、EF クエリが実際の IQueryable を列挙するとき (この場合は foreach) にのみ呼び出されるためです。つまり、IQueryable は何の作業も行っておらず、怠惰です。foreach が実際に情報を必要とする場合、IQueryable はそれを取得しますが、その時点でコンテキストは既に破棄されています。

次に、別のコンテキストを使用しているため、取得したばかりのポーズで作業を行うことはできません。EF は、post オブジェクトがまだ古いコンテキストにアタッチされていることを訴えます。この問題では、より一般的なパターンが強調されます。「作業単位」の間、コンテキストを保持する必要があります。一般的に言えば、DbContext.SaveChanges() を呼び出した後に破棄します。

これにより、3番目のバグにつながります。DbContext.SaveChanges() を呼び出しませんでした。このコードでは何も起こりません。データベースへの書き込みは行われず、EF 検証も実行されず、スコープ外に出ようとしているメモリ以外では何も起こりません。

最後に、テスト メソッドが副作用を引き起こしています (正常に保存されると仮定します)。これは、さらに先の課題につながります。これを回避する方法は、テスト前にトランザクションを開始し、テスト後にロールバックすることです。

その道を進みたい場合は、MbUnit がトランザクション ロジックを実行することを理解しています。

要約すると、これは私がやろうとしていることです...あなたが達成しようとしていることを私が理解していると仮定すると-

スタイルの観点から、私は CRUD メソッドが嫌いです。そのためのLinqがある場合、GetPostByNameのようなメソッド。私が抱えている問題には、維持するコードの増加が含まれます (nHibernate に切り替える場合は、GetPostByName を再実装する必要があります)。第二に、GetPostByDescript、GetPostById、GetPostByDescriptionOrName などが必要であることがわかります。

PS。ThePostのような名前で、あなたは単一のインスタンスを求めていると思います....

[Rollback]
[TestMethod]
public void CreatePostAddComment()
{

    using(var  _context = new BlogRepository())
    {
        _context.AddPost(GetTestPost());
        _context.SaveChanges();
        var thePost = _context.Posts.First(x => x.Name =="TestPost1");
        _dbContext.AddComment(GetTestComment(post));
        _context.SaveChanges();
    }
}
于 2013-02-10T19:02:13.657 に答える