DataContext在Dispose后被访问

17

我正在使用ASP.NET 4.0。

我有以下的代码,返回了一个错误:"Cannot access a disposed object. Object name: 'DataContext accessed after Dispose.'."

 public IEnumerable<BatchHeader> GetHeaders()
            {
                using(NSFChecksDataContext context = DataContext)
                {
                    IEnumerable<BatchHeader> headers = (from h in context.BatchHeaders
                                                        select h);                
                    return headers;                            
                }
            }

如果我将这个改为:

public IEnumerable<BatchHeader> GetHeaders()
        {
            using(NSFChecksDataContext context = DataContext)
            {            
                return context.BatchHeaders.ToList();                            
            }
        }

它会正常工作。我正在使用这种方法来填充一个RadGrid。有人可以解释一下为什么第二种方法可以工作,但第一种方法不能吗?

谢谢。


一篇不错的文章,解释了惰性求值:http://blogs.msdn.com/b/pedram/archive/2007/06/02/lazy-evaluation-in-c.aspx - Corbin March
1
有许多文章主张只允许DataContext自然垃圾回收,而不是调用Dispose。我意识到这会冒犯纯粹主义者,但是...请参见http://leedumond.com/blog/about-disposing-the-datacontext/和http://lee.hdgreetings.com/2008/06/linq-datacontex.html。 - Robert Harvey
请参见https://dev59.com/-HRA5IYBdhLWcg3wzhNY。 - Robert Harvey
3个回答

17

第一种方式不起作用是因为当该方法返回时,在using块中实例化的数据上下文已被释放。然而,返回的IEnumerable<BatchHeader>是惰性求值的,并且需要该数据上下文处于活动状态才能枚举其结果。

你可以像这样做:

 public IEnumerable<BatchHeader> GetHeaders() {
     using(NSFChecksDataContext context = DataContext) {         
         foreach(var header in context.BatchHeaders) {
             yield return header;
         }
     }
 }

第二个代码块有效是因为在数据上下文被释放之前,查询结果已经被枚举并存储在内存中。在此之后,不再需要数据上下文。但是,在使用像第二个代码块这样的代码时要小心;如果 BatchHeaders 表很大,则将其全部拉入内存。

现在,这是我回答中最严肃的部分:我绝对不能忍受实例化数据上下文来执行查询的情况。我想知道和控制我的数据上下文何时被使用。


4
Jason,你的回答确实解决了我的问题。你能否详细说明一下你所说的无法忍受的事情,并解释一下为什么你不能忍受它?我想学习好的做法... - Aaron
12
为什么?我绝对不能忍受看到实例化数据上下文以执行查询。我想知道并控制何时使用我的数据上下文。 为什么? - Robert Harvey

1
我猜测你的上下文中的IEnumerable使用了延迟执行,所以除非你使用ToList强制枚举,否则它不会在你使用值之前执行,而在这种情况下,你使用的是using块之外的值,因此对象将被处理。

-1
return headers.AsEnumerable(); 

应该能够工作,因为默认情况下,linq查询返回一个IQueryable对象,这意味着数据在使用foreach、ToArray、ToList或AsEnumerable枚举之前不会从数据库中提取。当Asp.Net尝试访问IQueryable并使用foreach提取数据时,连接已经关闭。


这很危险。你不知道表有多大,这会将其全部加载到内存中。 - jason
@Jason 它比你的答案更安全吗?据我所知,它们都会将整个表格加载到内存中。 - Bobson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接