Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set ViewBag property in the constructor of a ASP.NET MVC Core controller

My theme has some sort of breadcrumb. The controller is always the category. To avoid repeat myself, I want to set it in the constructor of the controller for all actions like this:

class MyController:Controller{
    public MyController() {
        ViewBag.BreadcrumbCategory = "MyCategory";
    }
}

When I access ViewBag.BreadcrumbCategory in the layout-view, its null. In a Action it works:

class MyController:Controller{
    public IActionResult DoSomething() {
        ViewBag.BreadcrumbCategory = "MyCategory";
    }
}

I'm wondering that setting a ViewBag property is not possible in a constructor? It would be annoying and no good practice to have a function called on every action which do this work. In another question using the constructor was an accepted answear, but as I said this doesn't work, at least for ASP.NET Core.

like image 585
Lion Avatar asked Oct 30 '16 14:10

Lion


People also ask

How pass ViewBag value from view to controller?

To pass the strongly typed data from Controller to View using ViewBag, we have to make a model class then populate its properties with some data and then pass that data to ViewBag with the help of a property. And then in the View, we can access the data of model class by using ViewBag with the pre-defined property.

What is the type of the ViewBag property?

In general, ViewBag is a way to pass data from the controller to the view. It is a type object and is a dynamic property under the controller base class.

How do I change the ViewBag value in Cshtml?

The ViewBag object value will be set inside Controller and then the value of the ViewBag object will be accessed in the cshtml file (View) using Razor syntax in ASP.Net MVC Razor.

How does ViewBag work in MVC?

The ViewBag in ASP.NET MVC is used to transfer temporary data (which is not included in the model) from the controller to the view. Internally, it is a dynamic type property of the ControllerBase class which is the base class of the Controller class.


2 Answers

There is an GitHub issue about it and it's stated that this is by design. The answer you linked is about ASP.NET MVC3, the old legacy ASP.NET stack.

ASP.NET Core is written from scratch and uses different concepts, designed for both portability (multiple platforms) as well as for performance and modern practices like built-in support for Dependency Injection.

The last one makes it impossible to set ViewBag in the constructor, because certain properties of the Constructor base class must be injected via Property Injection as you may have noticed that you don't have to pass these dependencies in your derived controllers.

This means, when the Controller's constructor is called, the properties for HttpContext, ControllerContext etc. are not set. They are only set after the constructor is called and there is a valid instance/reference to this object.

And as pointed in the GitHub issues, it won't be fixed because this is by design.

As you can see here, ViewBag has a dependency on ViewData and ViewData is populated after the controller is initialized. If you call ViewBag.Something = "something", then you it will create a new instance of the DynamicViewData class, which will be replaced by the one after the constructor gets initialized.

As @SLaks pointed out, you can use an action filter which you configure per controller.

The following example assumes that you always derive your controllers from Controller base class.

public class BreadCrumbAttribute : IActionFilter
{
    private readonly string _name;

    public BreadCrumbAttribute(string name)
    {
        _name = name;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        base.OnActionExecuting(context);

        var controller = context.Controller as Controller;
        if (controller != null) 
        {
            controller.ViewBag.BreadcrumbCategory = _name;
        }
    }
}

Now you should be able to decorate your controller with it.

[BreadCrumb("MyCategory")]
class MyController:Controller
{
}
like image 118
Tseng Avatar answered Sep 28 '22 04:09

Tseng


I have the same issue and solve it overriding the OnActionExecuted method of the controller:

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        base.OnActionExecuted(context);
        ViewBag.Module = "Production";
    }
like image 33
FcoJavier99 Avatar answered Sep 28 '22 03:09

FcoJavier99