views:

966

answers:

2

I am using "Single data context per atomic operation" approach while using Linq to Sql in an ASP.NET MVC application.

So far, I have been using a singleton datacontext, which I learnt has many issues, so I refactored the code to use single datacontext per atomic operation.

An example of a controller action is now as follows (refactoring has not changed this):

 public ActionResult List()
 {
  List<Request> requests = this.repository.AllRequests();
  return View(requests);
 }

repository is of type IRepository. I want to keep this abstraction to be able to switch to a different data layer (based on my recent experience with Linq to Sql, this may happen very soon :))

The LinqRepository implements the AllRequests() method as follows:

 public List<Request> AllRequests()
 {
  using (DataModelDataContext connection = GetContext())
  {
   return connection.Requests.ToList();
  }
 }

(just for reference, previously DataContext instance was a field of the LinqRepository and LinqRepository was held as a single static instance)

The DataContext is disposed before the method returns.

The view code now throws ObjectDisposed exceptions when accessing deferred properties:

            <%= Html.Encode(request.Branch.Name) %> //throws

I learnt disposing of the DataContext may not be needed (here http://stackoverflow.com/questions/389822/when-should-i-dispose-of-a-data-context)

When I do not dispose the DataContext (remove the using), there is no ObjectDisposedException.

i.e.: I altered the method as follows:

 public List<Request> AllRequests()
 {
  DataModelDataContext connection = GetContext();
  return connection.Requests.ToList();
 }

But I am wondering, what are the implications of not disposing DataContext instance in this scenario?

I know I am supposed to read all the data from the entity instance (including deferred properties) before the DataContext is disposed, but I would not like to introduce another abstraction (another Request class, copying all the properties over to it).

My questions:

Does the entity object hold a strong reference to its parent DataContext, preventing it from being GC-ed? (I guess it does, juts want to be 100% sure)

Can you provide advice on recommended approaches when using Linq to Sql while data layer abstraction needs to be retained? (including partial property updates)

Is there an open source project using a repository abstraction implementing Linq to Sql in ASP.NET MVC?

+1  A: 

My solution would be to have the Repository implement IDisposable. Then I would instantiate the Repository as an instance variable in the controller in the constructor in OnActionExecuting. Then I would dispose of the repository in OnResultExecuted. By the time you get to OnResultExecuted the result has already been rendered and any data needed has been consumed. The repository would similarly instantiate the DataContext in it's constructor and dispose of it when it is disposed.

tvanfosson
+3  A: 

First, let's consider what is happening to you.

You are:

  • Opening a data context.
  • Getting some data back.
  • Closing the data context.
  • Trying to read the data you got back from the data context.

That scenario is designed to work. However, for that to always work, all data would have needed to be eager loaded.

By default, LinqToSql will lazy (aka defer) load things as it sees fit. In this case, it appears that Request.Branch or at least Request.Branch.Name is being lazy loaded. For something to lazy load properly, the data context must still be around, which in your scenario, it no longer is.

Here are some options to get your scenario working:

Option 1:

Somehow "force" Request.Branch.Name to be loaded before disposing your data context. That is possible with LinqToSql, although not necessarily as easy or convenient as you'd want it to be. This should allow you to continue using your "single data context per atomic operation" pattern, although there are other downsides with that approach. The data context enables important features around change tracking, caching, and other scenarios and while you want to dispose of the data context as soon as possible, there are significant advantages to keeping it open so you should be open minded in regards to keeping your data context open longer than you currently are keeping it open. One way to implement option 1 is to use DTOs as you allude to when you say "another Request class", but that may be overkill for you here in this scenario.

Option 2:

Open the data context when your user starts their action and dispose it when the user ends their action. This is called the UnitOfWork pattern. This allows lazy loading to work properly as well as giving you change tracking and other nice ORM features. It can be a little more challenging to implement, but it's the most likely to work in the most scenarios.

Tvanfosson's answer lies somewhere between Options 1 & 2 and there are plenty of middle ground opportunities to solve your problem. All ORMs have a learning curve and LinqToSql is no different. Most ORMs have something similar to LinqToSql's data context and switching away from LinqToSql won't absolve you from having to understand how and why the data context works as it does.

You never want to open a data context without disposing it. That is a resource leak and it's very bad.

I'm pretty sure the NerdDinner MVC sample application uses both LinqToSql and the repository pattern, but I can't remember if it implements UnitOfWork for data context management.

Michael Maddox
Thank you for your answer. I was confusing unit of work with atomic operation and you provided some insight into that. However, are you sure on this: "You never want to open a data context without disposing it. That is a resource leak and it's very bad."? This post from Jon Skeet says the exact opposite:http://stackoverflow.com/questions/389822/when-should-i-dispose-of-a-data-context/389871#389871 "you don't really need to dispose of them in most cases - and that's by design"
Marek
I read the Jon Skeet/Matt Warren text. It seems pretty clear from Matt Warren's text that you are leaking resources if you don't dispose a data context. Jon Skeet then downplays the importance of that resource leak. It's a best practice to never intentionally leak resources. I can't help any more than that, sorry.
Michael Maddox
`DataContext` is designed to be lightweight and in a pool with it's lightweight brothers when lots is going on. You should not have to worry about disposing a LinqToSql `DataContext` and because of threading and the Deferred Execution problems, it's prob best practise not to. I use the StructureMap IoC container to look after my `DataContext`'s on a per request basis.
cottsak
cottsak, I disagree that it's a best practice not to dispose the DataContext.
Michael Maddox
@Michael Maddox: Without going link-nutty it's generally ok to dispose a DC but i would not suggest it unless you're debugging a problem you think is connection/thread related. Disposing a DC can lead to race conditions in multiple threads if you're doing it 'just coz you can'. So i'd suggest you let the DC dispose itself in most cases.
cottsak