Mvc: Unable to set `ViewBag` or `ViewData` entries from `Controller` constructors

Created on 21 Oct 2014  路  5Comments  路  Source: aspnet/Mvc

The following works in MVC 5.2 but throws an InvalidOperationException in vNext. This prevents common initialization scenarios unless the user is aware of OnActionExecuting() or OnActionExecutionAsync() (which are less discoverable than MVC 5.2's Initialize() method). ViewData["Items"] doesn't help because ViewData is null.

``` c#
public class HomeController : Controller
{
private static readonly IEnumerable _items =
new SelectList(Enumerable.Range(7, 13));

public HomeController()
{
ViewBag.Items = _items;
}
...
}
```

Details:
The root cause is DefaultControllerActivator creates the controller's ViewDataDictionary instance. So ViewData is null until Controller instances are activated. Worse if ViewBag is used while ViewData is still null, the result is an InvalidOperationException thrown from the DynamicViewData.ViewData getter.

By contrast, few properties in the MVC 5.2 Controller class even return null (let alone throw) if invoked from the constructor. (Exceptions include ControllerContext, Url, and anything ControllerContext-derived e.g. HttpContext and User. All of those simply return null prior to the Initialize() call.)

Probably need to revisit the Controller activation model. At least should look at the properties lazy-initialized in MVC 5.2 and decide if they should be handled similarly in vNext.

wontfix

Most helpful comment

This behavior is intentional do not do work in your controller's constructor that touches anything stateful or related to the current request. If you want to run some code before any action is invoked, override the OnActionExecuting method defined on Controller.

All 5 comments

If we get significant user feedback on this issue we can consider adding a more useful error message, but this behavior is intentional now that we have activated controllers.

In ASP.NET Core 1.0 I tried to set a page-category title using ViewBag in the constructor of my controller:

class MyController : Controller {
    public MyController() {
        ViewBag.PageCategory = "Test";
    }
}

I use ViewBag.PageCategory in my layout-razor view. When I set it in a single action it works, but not in the constructor. It's not working without any exception or error message.

This behavior is intentional do not do work in your controller's constructor that touches anything stateful or related to the current request. If you want to run some code before any action is invoked, override the OnActionExecuting method defined on Controller.

@rynowak Thanks, that worked! As an alternative, its also possible to create a DataAnnotation-Attribute. For example, by inheriting from ResultFilterAttribute. This also allow to override the OnActionExecuting method.

@DMWOO7 - yes, that would work as well, but it doesn't have anything to do with System.ComponentModel.DataAnnotations.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LiangZugeng picture LiangZugeng  路  3Comments

kspearrin picture kspearrin  路  4Comments

KLuuKer picture KLuuKer  路  3Comments

hikalkan picture hikalkan  路  4Comments

Lutando picture Lutando  路  4Comments