views:

382

answers:

2

I have a property on my BaseController called DataContext that holds my LINQ to SQL data context (or fake context for testing). When using a parameterless constructor (in other words, when a request to ASP.NET MVC is made), a new instance of my LINQ to SQL data context is assigned to the property:

public class BaseController : Controller {
    public IDataContextWrapper DataContext { get; set; }

    public BaseController() : this(new DataContextWrapper<MyDataContext>()) { }

    public BaseController(IDataContextWrapper context) {
        DataContext = context;
    }
}

Also in my BaseController, I set some global ViewData items:

protected override void OnActionExecuting(ActionExecutingContext filterContext) {
    ViewData["Example"] = DataContext.Table<Example>().Count();
    base.OnActionExecuting(filterContext);
}

This is working fine for almost every action. The only one that doesn't work is the Logout action on my AccountController:

public ActionResult Logout() {
    FormsAuth.SignOut();
    return RedirectToResult("Login");
}

This raises a NullReferenceException during BaseController.OnActionExecuting. When executing that particular action, the DataContext property is null.

Why would this only occur on one action?

Note: IDataContextWrapper and DataContextWrapper simply wraps the existing functionality of the LINQ to SQL DataContext object so that it can be replaced with a fake context in unit tests. It doesn't do any disposing on its own, but leaves it up to the underlying DataContext, so I'm pretty certain that's not the problem.

A: 

To follow up my comment, check out this link and more specifically the link Microsoft documentation here which state:

In general, a DataContext instance is designed to last for one "unit of work" however your application defines that term. A DataContext is lightweight and is not expensive to create. A typical LINQ to SQL application creates DataContext instances at method scope or as a member of short-lived classes that represent a logical set of related database operations.

Microsoft did a terrible job explaining this and frankly explaining using Linq in an n-tier environment in the first place. In my particular case, I had one (static) datacontext implemented via Singleton pattern, which I am guessing is what you have done as well. ( As it is the most logical design, IMHO ). This however, is extremely NOT the way to do things. In my case, the fix was actually pretty easy, changing my GetDataContext() call to return a new DataContext every time, instead of returning the static instance. This however, you will find, creates a whole new crop of problems. None of them are insurmountable once you figure them out, but definitely a pain.

If you have such a setup ( Singleton accessors for your DataContext), change it to see if it fixes your problem.

Regardless, do not use a global DataContext, nor persist a DataContext if dealing with an n-tier architecture.

Even if this doesn't solve your particular problem, I highly suggest you re-architect your solution to make DataContexts have a unit of work lifespan, if it hasn't bitten you already, it will.

Serapth
My system works in the same way that the Nerd Dinner sample on CodePlex does. Each time a controller is created, it gets a new DataContext. It isn't a static singleton (as you can see in my code). If the DataContext was being disposed before I access it, wouldn't I receive an ObjectDisposedException instead of a NullReferenceException? The NullReferenceException makes me think that the DataContext property isn't even getting initialized in the first place, which would appear to be impossible.
David Brown
A: 

For reasons that I don't quite understand, when a new AccountController is created for the Logout action, ASP.NET MVC is using the second constructor with a null parameter (could be a bug?). I changed the class to create a new default DataContext when the parameter is null:

public class BaseController : Controller {
    public IDataContextWrapper DataContext { get; set; }

    public BaseController() : this(null) { }

    public BaseController(IDataContextWrapper context) {
        DataContext = dataContext ?? new DataContextWrapper<MyDataContext>();
    }
}

Now it works.

It strikes me as strange that ASP.NET MVC used the default constructor in some cases, and an overload in others, though. Can anyone shed some light on this?

David Brown
MVC should always use the default parameterless constructor unless told otherwise by a DI framework and/or a Controller factory registered in global.asax with `SetControllerFactory` - are you doing any of this?
cottsak