17

I'm using ASP.NET 4.0.

I've got the following code that returns with an error of "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;                            
                }
            }

If I change this to:

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

It will work fine. I'm using this method to populate a RadGrid. Can anyone explain why the second method will work but not the first?

Thanks.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Aaron
  • 7,431
  • 12
  • 35
  • 37
  • A decent article explaining lazy evaluation: http://blogs.msdn.com/b/pedram/archive/2007/06/02/lazy-evaluation-in-c.aspx – Corbin March Dec 01 '10 at 20:17
  • 1
    There are many articles that advocate just allowing the `DataContext` to garbage collect naturally, rather than calling `Dispose` on it. I realize this offends the purists, but... See http://leedumond.com/blog/about-disposing-the-datacontext/ and http://lee.hdgreetings.com/2008/06/linq-datacontex.html – Robert Harvey Dec 01 '10 at 20:26
  • See also http://stackoverflow.com/questions/821574/c-linq-to-sql-should-datacontext-be-disposed-using-idisposable – Robert Harvey Dec 01 '10 at 20:49

3 Answers3

17

The first doesn't work because when the method returns the data context instantiated in the using block is disposed. However, the IEnumerable<BatchHeader> returned is lazily evaluated and needs this data context to be alive to enumerate its results.

You could do something like this:

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

The second block works because the query results are enumerated over and stored in memory before the data context is disposed of. After that happens, the data context isn't needed anymore. However, be careful when using code like your second block; if the BatchHeaders table is large you just pulled it all into memory.

Now, and here is the most serious part of my answer: I absolutely can't stand seeing queries that instantiate data contexts to execute. I want to know and control when my data contexts are being used.

jason
  • 236,483
  • 35
  • 423
  • 525
  • 4
    Jason, your answer did correct my issue. Could you please elaborate on what you said you can't stand and explain why exactly you can't stand it? I want to learn good practices... – Aaron Dec 01 '10 at 20:19
  • 12
    `I absolutely can't stand seeing queries that instantiate data contexts to execute. I want to know and control when my data contexts are being used.` -- Why? – Robert Harvey Dec 01 '10 at 20:19
1

I'm guessing the IEnumerable from your context is using deferred execution, so unless you force it to enumerate using ToList it doesn't do so until you use the values, which in this case is outside of the using block so the object will be disposed.

Steven Robbins
  • 26,441
  • 7
  • 76
  • 90
-1
return headers.AsEnumerable(); 

should work, because by default, a linq query returns an IQueryable object, which means that the data is not fetched from db until enumerated using foreach, ToArray, ToList or AsEnumerable. When Asp.Net tried to access the IQueryable and fetch the data using foreach, the connection was already closed.

Davita
  • 8,928
  • 14
  • 67
  • 119
  • This is dangerous. You have no idea how large the table is and this would pull it all into memory. – jason Dec 01 '10 at 20:12
  • @Jason Is it any less dangerous than your answer? As far as I can tell, they both pull the entire table into memory. – Bobson Dec 15 '11 at 21:35