1

C# 6.0 in a Nutshell by Joseph Albahari and Ben Albahari (O’Reilly).

Copyright 2016 Joseph Albahari and Ben Albahari, 978-1-491-92706-9.

brings, at page 376, a discussion on disposing DataContext/ObjectContext instances.

Disposing DataContext/ObjectContext

Although DataContext/ObjectContext implement IDisposable, you can (in general) get away without disposing instances. Disposing forces the context’s connection to dispose—but this is usually unnecessary because L2S and EF close connections automatically whenever you finish retrieving results from a query. Disposing a context can actually be problematic because of lazy evaluation. Consider the following:

IQueryable<Customer> GetCustomers (string prefix)
{
   using (var dc = new NutshellContext ("connection string"))
   return dc.GetTable<Customer>()
   .Where (c => c.Name.StartsWith (prefix));
}

...
foreach (Customer c in GetCustomers ("a"))
Console.WriteLine (c.Name);

This will fail because the query is evaluated when we enumerate it—which is after disposing its DataContext.

There are some caveats, though, on not disposing contexts.

(and it goes on to list them...)

At the end, to avoid the exception just described, it states:

If you want to explicitly dispose contexts, you must pass a DataContext/ObjectContext instance into methods such as GetCustomers to avoid the problem described.

The question:

I do not get what the author meant. (no example followed).

I mean, does the author's says you can have the method still return an IQueryable<Customer>, dispose of the DataContext parameter and keep deferred execution altogether ?

How is this achieved ? I can see it happening only if giving up lazy loading.

Veverke
  • 9,208
  • 4
  • 51
  • 95
  • Instead of returning the `IQueryable` it would probably be better to return a `List` which will execute the query the moment you call `.ToList()`. The OTB way for MVC5 to handle this is to call `db.Dispose()` when the Controller is getting disposed. – Eonasdan Aug 08 '16 at 16:49
  • @Eonasdan: thanks your your input. But you are then corroborating my assumption that the author must be implying giving up lazy loading. I am looking for an affirmation that indeed it is not possible to have a function dispose a context while returning back a sequence which enumeration will still be deferred. – Veverke Aug 08 '16 at 16:53
  • Yeah, not that I know of. The author is suggesting you do `GetCustomers("a", db)` where `db` is an existing context you newed up elsewhere. That would prevent you from needing to wrap `db` in a `using` statement inside `GetCustomers` – Eonasdan Aug 08 '16 at 16:58
  • @Eonasdan: this does not make much sense, since you then give up on disposing the context - which contradicts his `If you want to explicitly dispose contexts`, meaning you can't claim that and suggest this. – Veverke Aug 08 '16 at 17:00
  • But earlier, it also mentions: `you can (in general) get away without disposing instances`. – Jeff Mercado Aug 08 '16 at 17:03
  • `you must pass a DataContext/ObjectContext instance into methods` I don't know what else he could mean by this. – Eonasdan Aug 08 '16 at 17:05
  • @JeffMercado: I believe the statement you mention refers to "you usually do not have to care about this, since these objects's close method is automatically run and it will dispose whatever is there to be disposed. The reason I am stubbornly rejecting yours and Eonasdan's point is the "there are some caveats, though, on not disposing contexts (which I realized was missing from the original post's text - have now corrected it). In a short, the author says: No need to worry about disposing these objects, they will do it with or without your consent. – Veverke Aug 09 '16 at 07:48
  • @JeffMercado: (cont.) However - there may still be some scenarios where explicit disposing may be beneficial (and it goes on to list them). So, at the end, the reader is likely to think: Ok, at the end of the day, explicit disposing may be beneficial in one or two rare occasions, making it "more beneficial" than implicit disposing (where you lose these edge case benefits). Great, you (the author) just made a point on explicitly disposing these objects, but what is not clear is: does it come at the expense of losing deferred execution ? – Veverke Aug 09 '16 at 07:48

2 Answers2

1

There is a conflict between the concept of Lazy Loading and the Repository pattern. The repository pattern, for which DataContext/ObjectContext are designed for, separate the code that accesses a database from the code that consumes your business objects.

The fundamental problem with lazy loading properties is that the business objects being returned by the data layer depend on and utilize technology specific data retrieval when it may not be expected.

Some examples:

The underlying data retrieval mechanism has been disposed of when trying to access lazy loading properties later. This is what the author is trying to explain.

Customer myCustomer;
using (var dataSource = GetRepository()) {
   myCustomer = dataSource.Retrieve("John");
}

// throws exception since the connection to
// the database has been closed already
var orders = myCustomer.Orders; 

You may have code somewhere in your UI which attempts to read from a certain property, which triggers a database call and slows down your UI. An SqlException may occur retrieving properties in unexpected places, leading to either unreliability or tight coupling between your data store and your consumer code.

// some business layer
Customer myCustomer = myRepository.GetCustomer("John");

...
// some UI component trying to show the customer's orders
var orders = myCustomer.Orders; 
// could throw any kind of data access exception, such as SqlException
// e.g. Wifi is not working anymore, now I have to build error
// handling for that here, even though it's not very obvious to someone
// who is just accessing the Orders property

Note that in my humble opinion, this is worse than having explicit coupling between data and logic layers, since the coupling is there, but hidden from view.

Bas
  • 26,772
  • 8
  • 53
  • 86
  • Hi Bas. You do explain how deferred execution could cause exceptions when you the underlying objects were disposed. This I already knew (this part I understood from the book), the question is - after the author makes the point of "You do not have to care about disposing these objects since their close method will automatically run and required disposes will occur. But, there are some edge cases when explicitly disposing them can be beneficial" – Veverke Aug 09 '16 at 08:00
  • (cont.) and goes on and says *If you want to explicitly dispose contexts, you must pass a DataContext/ObjectContext instance into methods such as GetCustomers to avoid the problem described. This is the part I do not understand. How is this to be done? Will this incur the expense of not being able to work in deferred execution anymore ? – Veverke Aug 09 '16 at 08:01
0

It's saying that you should create a data context object once and pass it it to the query methods for use.

Something like:

IQueryable<Customer> GetCustomers (NutshellContext dc, string prefix)
{
   return dc.GetTable<Customer>()
       .Where (c => c.Name.StartsWith (prefix));
}

Then when you call that method, pass in the data context you had created. You should only dispose that context when you're shutting down.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272