Why is dbcontext disposed before the foreach loop

戏子无情 提交于 2019-12-12 05:17:12

问题


I've been struggling trying to figure out why this code disposes dbcontext before the foreach loop. Any clues would be greatly appreciated. Thanks!

    [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));
            }

        }
    }

回答1:


The using statement is in fact syntactic sugar it is equivalent to : -

[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));
        }

    }
}

However there are several key problems with your code, only the first of which is the Context Disposed Exception.

Your first issue about the context being disposed is because the EF query is invoked only when you enumerate the actual IQueryable, in this case, at the foreach. In other words, the IQueryable has done no work and is lazy. When the foreach actually NEEDS the information the IQueryable goes to fetch it, by which time the context has already been disposed.

Secondly you can't do work on the pose you just got, because you are using another context. EF will complain that the post object is still attached to the old context. This issue will highlight a more general pattern. You should hold your context for as long as your "unit of work". Generally speaking you dispose after you call DbContext.SaveChanges().

This leads me to your third bug. You didn't call DbContext.SaveChanges(). Nothing is going to happen with this code. You are not going to write to the database, EF validation isn't going to run, nothing will happen except in memory, which is about to go out of scope.

Finally your test method is producing side effects (assuming it will successfully save). This will lead to pain points further down the line. A way to get around this is to begin a transaction before the test, and rollback after the test.

I understand that MbUnit will do the transaction logic for you if you wish to go down that road.

So to sum up, this is what I WOULD do...assuming I understand what you are trying to achieve-

From a stylistic point I hate CRUD methods. Methods like GetPostByName, when you have Linq for that. The problems I have with it include, more code to maintain (if you change over to nHibernate, you need to reimplement GetPostByName). Secondly you will then next find you need a GetPostByDescript, then a GetPostById then a GetPostByDescriptionOrName...etc etc...

PS. With a name like ThePost I assume you are after a single instance....

[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();
    }
}


来源:https://stackoverflow.com/questions/14801266/why-is-dbcontext-disposed-before-the-foreach-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!