Update – The answer is apparently that DbLinq doesn’t implement Dispose() properly. D’oh!
The below is all sort of misleading – Bottom line: DbLinq is not (yet) equivalent to LinqToSql, as I assumed when I originally asked this question. Use it with caution!
I’m using the Repository Pattern with DbLinq. My repository objects implement IDisposable, and the Dispose() method does only thing–calls Dispose() on the DataContext. Whenever I use a repository, I wrap it in a using block, like this:
public IEnumerable<Person> SelectPersons()
{
using (var repository = _repositorySource.GetPersonRepository())
{
return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person>
}
}
This method returns an IEnumerable<Person>, so if my understanding is correct, no querying of the database actually takes place until Enumerable<Person> is traversed (e.g., by converting it to a list or array or by using it in a foreach loop), as in this example:
var persons = gateway.SelectPersons();
// Dispose() is fired here
var personViewModels = (
from b in persons
select new PersonViewModel
{
Id = b.Id,
Name = b.Name,
Age = b.Age,
OrdersCount = b.Order.Count()
}).ToList(); // executes queries
In this example, Dispose() gets called immediately after setting persons, which is an IEnumerable<Person>, and that’s the only time it gets called.
So, three questions:
- How does this work? How can a disposed
DataContextstill query the database for results after theDataContexthas been disposed? - What does
Dispose()actually do? - I’ve heard that it is not necessary (e.g., see this question) to dispose of a
DataContext, but my impression was that it’s not a bad idea. Is there any reason not to dispose of a DbLinqDataContext?
It doesn’t work. There’s something you’re not showing us. I’m guessing that either your repository class doesn’t dispose the
DataContextproperly/at the right time, or that you are perfunctorily writingToList()at the end of every query, which completely negates the query transformation and deferred execution you normally get.Try the following code in a test app, I guarantee you that it will throw an
ObjectDisposedException:This is the simplest possible reproducible case, and it will always throw. On the other hand, if you write
people = context.Person.ToList()instead, then the query results have already been enumerated inside theusingblock, which I’ll bet is what’s happening in your case.Among other things, it sets a flag indicating that the
DataContextis disposed, which is checked on every subsequent query and causes theDataContextto throw anObjectDisposedExceptionwith the messageObject name: 'DataContext accessed after Dispose.'.It also closes the connection, if the
DataContextopened it and left it open.It is necessary to
DisposetheDataContext, as it is necessary toDisposeevery otherIDisposable. You could potentially leak connections if you fail to dispose theDataContext. You could also leak memory if any of the entities retrieved from theDataContextare kept alive, since the context maintains an internal identity cache for the unit-of-work pattern it implements. But even if none of this were the case, it is not your concern what theDisposemethod does internally. Assume that it does something important.IDisposableis a contract that says, “cleanup may not be automatic; you need to dispose me when you’re finished.” You have no guarantees of whether or not the object has its own finalizer that cleans up after you if you forget toDispose. Implementations are subject to change, which is why it’s not a good idea to rely on observed behaviour as opposed to explicit specifications.The worst thing that can happen if you dispose an
IDisposablewith an emptyDisposemethod is that you waste a few CPU cycles. The worst thing that can happen if you fail to dispose anIDisposablewith a non-trivial implementation it is that you leak resources. The choice here is obvious; if you see anIDisposable, don’t forget to dispose it.