In this tip, I demonstrate how you can dispose of a DataContext within an ASP.NET MVC controller. Next, I argue that there is no compelling reason to do this.
Several people have emailed me recently with the same question about whether or not it is important to call Dispose() on the DataContext object. In all of the code samples that I write, I create an instance of a DataContext object, but I never properly dispose of it. Is this wrong?
The DataContext class implements the IDisposable interface. In general, if a class implements the IDisposable interface, then that is good evidence that you should call Dispose(). But keep reading.
Classes that implement the IDisposable interface typically use resources that cannot be cleaned up by the .NET framework garbage collector. Calling the IDisposable.Dispose() method executes code that explicitly releases a precious resource back into the world.
A prime example of a class that implements the IDisposable interface is the SqlConnection class. A SqlConnection class uses a Microsoft SQL Server database connection. Because SQL Server supports a limited number of connections, it is important to release a connection as quickly as possible.
Typically, you do not call the Dispose() method directly. Typically, you take advantage of a Using statement in your code like this:
C# Code
using (var con = new SqlConnection(conString)) { var cmd = new SqlCommand("SELECT * FROM Products"); var reader = cmd.ExecuteReader(); }
VB.NET Code
Using con = New SqlConnection(conString) Dim cmd = New SqlCommand("SELECT * FROM Products") Dim reader = cmd.ExecuteReader() End Using
A Using statement calls the Dispose() method at the end of the Using block automatically. The Using statement calls the Dispose() method even if there was an error in the code.
Dispose of the DataContext
Because the DataContext implements IDisposable, it would seem like the right way to use the DataContext in a controller is like Listing 1. In Listing 1, a DataContext is used within a Using statement.
Listing 1 – BadController.cs
using System.Linq; using System.Web.Mvc; using MvcFakes; using Tip34.Models; namespace Tip34.Controllers { public class BadController : Controller { private IDataContext _dataContext; public BadController() : this(new DataContextWrapper("dbcon", "~/Models/Movie.xml")){ } public BadController(IDataContext dataContext) { _dataContext = dataContext; } public ActionResult Index() { using (_dataContext) { var table = _dataContext.GetTable<Movie>(); var movies = from m in table select m; return View("Index", movies); } } } }
The controller in Listing 1 is taking advantage of the IDataContext, DataContextWrapper, and FakeDataContext objects discussed in the previous tip:
/blog/archive/2008/08/16/asp-net-mvc-tip-33-unit-test-linq-to-sql.aspx
By taking advantage of these objects, you can easily test your MVC application.
Within the Index() method, the DataContext is used within a Using statement. The Using statement causes the Dispose() method to be called on the DataContext object (The DataContextWrapper.Dispose() method delegates to the DataContext.Dispose() method).
You can use the controller in Listing 1 with a typed view. The typed view casts the ViewData.Model property to an instance of IQueryable. The code-behind for the typed view is contained in Listing 2.
Listing 2 — ViewsBadIndex.aspx.cs
using System.Linq; using System.Web.Mvc; using Tip34.Models; namespace Tip34.Views.Bad { public partial class Index : ViewPage<IQueryable<Movie>> { } }
Unfortunately, however, the code in Listing 1 does not work. If you try to execute this code, you get the error displayed in Figure 1. You receive the error: “Cannot access a disposed object”. The problem is that the DataContext has already been disposed before the DataContext can be used in the view.
Figure 1 – Error from early disposal
One of the big benefits of using LINQ to SQL is that it supports deferred execution. A LINQ to SQL statement is not actually executed against the database until you start iterating through the results. Because the movie records are not accessed until they are displayed in the Index view, the DataContext is disposed before the records are retrieved from the database and you get the error.
One easy way to fix the problem with the controller in Listing 1 is to pass the movies as a list instead of as an IQueryable. The modified Index() action method in Listing 3 does not cause an error:
Listing 3 – HomeController.cs
using System.Linq; using System.Web.Mvc; using MvcFakes; using Tip34.Models; namespace Tip34.Controllers { public class HomeController : Controller { private IDataContext _dataContext; public HomeController() : this(new DataContextWrapper("dbcon", "~/Models/Movie.xml")){ } public HomeController(IDataContext dataContext) { _dataContext = dataContext; } public ActionResult Index() { using (_dataContext) { var table = _dataContext.GetTable<Movie>(); var movies = from m in table select m; return View("Index", movies.ToList()); } } public ActionResult Details(int id) { using (_dataContext) { var table = _dataContext.GetTable<Movie>(); var movie = table.SingleOrDefault(m=>m.Id == id); return View("Details", movie); } } } }
The only difference between the Index() method in Listing 3 and the Index() method in Listing 4 is that the ToList() method is called on the movies before the movies are passed to the view.
The controller in Listing 3 also contains a Details() action that displays details for a particular movie. You do not need to do anything special when retrieving a single database record. LINQ to SQL does not used deferred execution when you retrieve a single record.
If you change the typed view so that it expects a list instead of an IQueryable, then everything works fine:
using System.Collections.Generic; using System.Web.Mvc; using Tip34.Models; namespace Tip34.Views.Home { public partial class Index : ViewPage<List<Movie>> { } }
The modified controller now works because the ToList() method forces the LINQ to SQL statement to be executed against the database within the controller and not within the view.
You can unit test the Home controller in Listing 3 just fine. The class in Listing 4 contains a unit test that verifies that the Index() method returns a set of database records.
Listing 4 — ControllersHomeControllerTest.cs
using System.Collections.Generic; using System.Web.Mvc; using Microsoft.VisualStudio.TestTools.UnitTesting; using MvcFakes; using Tip34.Controllers; using Tip34.Models; namespace Tip34Tests.Controllers { [TestClass] public class HomeControllerTest { [TestMethod] public void Index() { // Create Fake DataContext var context = new FakeDataContext(); var table = context.GetTable<Movie>(); table.InsertOnSubmit(new Movie(1, "Star Wars")); table.InsertOnSubmit(new Movie(2, "King Kong")); context.SubmitChanges(); // Create Controller var controller = new HomeController(context); // Act ViewResult result = controller.Index() as ViewResult; // Assert var movies = (List<Movie>)result.ViewData.Model; Assert.AreEqual("Star Wars", movies[0].Title); } } }
This unit test is taking advantage of the FakeDataContext class described in the previous tip to create an in-memory version of a DataContext. First, the unit test adds some fake database records to the FakeDataContext. Next, the Index() action is invoked and a result is returned. Finally, the ViewData contained in the result is compared against the fake database records. If the ViewData contains the first fake database record then the Index() action works correctly and success is achieved.
Don’t Dispose of the DataContext
In the previous section, I demonstrated how you can Dispose() of a DataContext within an MVC controller action. In this section, I question the necessity of doing this.
When you call the DataContext Dispose() method, the DataContext delegates the call to the SqlProvider class. The SqlProvider class does two things. First, the SqlProvider calls the ConnectionManager.DisposeConnection() method which closes any open database connection associated with the DataContext. Second, the SqlProvider sets several objects to null (including the ConnectionManager).
So, the most important consequence of calling the DataContext.Dispose() method is that any open connections associated with the DataContext get closed. This might seem really important, but it’s not. The reason that it is not important is that the DataContext class already manages its connections. By default, the DataContext class opens and closes a connection automatically.
The DataContext object translates a LINQ to SQL query into a standard SQL database query and executes it. The DataContext object opens a database connection right before executing the SQL database query and closes the connection right after the query is executed (the connection is closed in the Finally clause of a Try..Catch block).
If you prefer, you can manage the opening and closing of the database connection used by a DataContext yourself. You can use the DataContext.Connection property to explicitly open a connection and explicitly close the connection. Normally, however, you don’t do this. You let the DataContext take care of itself.
Most often, when you call the Dispose() method on the DataContext object, any database connection associated with the DataContext object is already closed. The DataContext object has closed the database connection right after executing the database query. So, the Dispose() method really doesn’t have anything to do.
Therefore, you really don’t get any huge benefits from calling Dispose() on the DataContext object. The only slight benefit is that the Dispose() method sets several objects to null so that they can be collected earlier by the garbage collector. Unless you are worried about every byte used by your application, or you are tracking a huge number of objects with your DataContext, I wouldn’t worry too much about the memory overhead of the DataContext object.
Summary
In this tip, I demonstrated how you can call the Dispose() method when working with a DataContext object within an MVC controller. Next, I argued that there really isn’t a very compelling reason to want to call the Dispose() method. I plan to continue to be lazy about disposing any DataContext objects that I used within my MVC controllers.
So, your suggesting I don’t use the ‘using’ that I currently use with the DataContext. I’ve always used ‘using’ with any object that implements IDisposable
i sure hope your data context isnt that “close” to your controller… 3 tier; your doing it wrong.
If exception occurred, do I need to dispose the datacontext manually?
The principle that you demonstrate here als goes for ADO.NET entity framework contexts. Cleaning an object context in that case may even cause some really strange behavior when creating a new context later on and trying to save and object with that context instead of the context the object was retrieved with.
See also my blogpost: blogs.infosupport.com/…/…pository-pattern.aspx
Your tip isn’t just a great tip, it’s a highly necessary tip as sooner or later developers will run into trouble with this 🙂
I’d argue that anything which is IDisposable should be explicitly disposed; you should treat any class as a black box and not make assumptions about its internal implementation, which might change in a future release.
@W.Meints: A context is only meant to be used for a single unit of work before being thrown away: iridescence.no/…/…he-Unit-of-Work-Pattern.aspx
I agree with Fredrik here. If the designer of the class has taken the time to make it implement IDisposable, then you should Dispose it, preferably with a using block. If you use Reflector to look at the internal implementation to make your Dispose decisions then you deserve to get burned when things change in the future. And trying to save a line of code (that’s all it is with using) is more silly than assuming those of us that do call Dispose are worried about saving a few bytes.
As a highly respected author and trainer I hope that you correct this and encourage best practices at all times.
From the lang reference: The using statement “Provides a convenient syntax that ensures the correct use of IDisposable objects. “
It seems to me that in this example, the HomeController class is only sometimes responsible for the dataContext resource: If the parameterless ctor is called, then HomeController should dispose the dataContext, if the datacontext is passed in, then it should not dispose it.
Andrew has it, in my opinion. Disposing an object is the responsibility of the object that created it. If you don’t follow this kind of rule, you’ll end up trying to use disposed objects at some point or another.
Thank you for this post, I had experienced the error you showed and I did get around it by using .ToList(), but I didn’t all of the whys.
Failing to Dispose an object which implements IDisposable typically results in the object going into the finalization queue (read Chapter 19 of Jeffrey Richter’s Applied Microsoft .NET Framework Programming for details). The results of this is that an object’s memory that might have otherwise been freed in generation 01 be freed until a later generation collection. If you’re creating a lot of these objects, well, do the math.
So you should always Dispose any object which implements IDisposable.
In the case of Controllers and DataContexts, this turns out to be really easy, because Controller also implements IDisposable, and hence has a virtual Dispose method you can override. So you don’t have to wrap your use of DataContext in a using. You can create it in the constructor (or wherever) and then dispose in the overridden Controller.Dispose. In this case, using IQueryable in the view works just fine, because the framework does not Dispose the Controller until the View has been rendered.
Great response and idea Craig.
@Craig – Every class with a finalizer ends up in the finalization queue (takes longer to garbage collect). A finalizer gives a class a “last ditch” opportunity to clean up its resources regardless of whether a programmer remembers to call Dispose().
Some classes that implement the IDisposable interface call GC.SuppressFinalize() within their Dispose() method which prevents the class from being added to the finalization queue. However, the DataContext class does not have a finalizer and its Dispose() method does not call GC.SuppressFinalize().
Therefore, there is no delay in garbage collecting a DataContext when you do not call Dispose().
You can read more at:
msdn.microsoft.com/…/b1yfkh5e.aspx
Having a finalizer or not is an implementation detail which can change. Will there be a ~DataContext in the next version of the framework? Who knows? It’s certainly possible. The MSDN article you cite recommends it, for example. Also, if you implement IDisposable and don’t have a finalizer (along with following the rest of the pattern explained in the article), FxCop will complain.
I would never want to count on a disposable class not having a finalizer for all time.
The unit of work-per-request (or data context/session) pattern is a really good way to go.
I blogged about an ASP.NET MVC implementation here:
http://www.mindscape.co.nz/…/using-the-unit-of-work-...
Great article thanks!
It’s just a shame some people seem a bit uptight about not disposing of an object. I agree that in most cases if an class implements IDisposable then it should be disposed of. But in this case Stephen has explained what happens to the DataContext object on disposing.
I agree with him that in most cases there’s no need (unless you want to send objects for GC as early as possible – again as stated by Stephen).
Thanks again!!
With all due respect this goes against anything anyone’s learned about separation of concerns and layering. Why would you pass a datacontext to a view and effectively let the view execute a sql call (which is what you’re doing by letting it be deferred)? This should be a library, and contexts should be properly disposed. The models passed to view should disconnected representations of the model. This might work for a scratch intranet app, but not for anything you’d get paid for to write. Sorry Stephen, this is sloppy.
@Claus H
Deferred execution and passing the queryable ‘upwards’ is also done in the MVC storefront project by Rob Conery. Lot’s of comments on it, but he persisted and he’s doing that ‘officially’ for Microsoft (as en employee, and it’s posted on http://www.asp.net). So I think that pattern is here to stay.
I think I agree with the majority vote here, disposing of disposable objects is the CORRECT way to do it. Just like any other technique that is analyzed if you see things black and white, do or die, then dispose of your objects every time. My preferred method of dealing, specifically with DataContexts is to use the “using” to wrap it up and use the DataLoadOptions object to specify what child/parent data I will want access to through my view. IMHO it should be preferred to do it this way, not only because of the proper disposal of the object, but it refines your selection. Take the example of using a string versus an enumeration/setting/etc. Just like you CAN manually use a string in code, I think general consensus is that you SHOULD use another approach. Thoughts??
/07/08/MVC-Routing-Security-Hole.aspx
Better support for database should not be over-used but at the same time, it is a required feature.
W Great article, though – thanks!
41fge Thank you for sharing the this code.
wow.. very nice info at this post thanks!! i really like it
5tr
I believe it is a promising (currently version 4.0). So I would stick with it.. thanks
I must admit, it was a nice article, very helpful. Thanks!–
free ads |employment |sleep number bed
As the users of HD Camcorders like Sony, Canon, Panasonic, this HD Converter is necessary to help us convert hd Video easily and quickly. The Converter for HD provides several practical editing functions to help you achieve ideal output effect. Trim function is to cut videos into clips which you can just convert and transfer to your player. Crop function helps you remove black bars around the movie. You could use Effect function to adjust video brightness, contrast, saturation and more parameters. More powerful and considerate functions are waiting for you to explore.MKV Converter l FLV Converter l DVD Ripper !.
Good read