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

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";
    }

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
{
}

Here is a better way to do this for .NET Core 3.x, use the ResultFilterAttribute:

Create your own custom filter attribute that inherits from ResultFilterAttribute as shown below:

   public class PopulateViewBagAttribute : ResultFilterAttribute
    {


        public PopulateViewBagAttribute()
        {
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            //   context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
            (context.Controller as MyController).SetViewBagItems();
            base.OnResultExecuting(context);
        }
    }

You'll need to implement the method SetViewBagItems to populate your ViewBag

public void SetViewBagItems() { ViewBag.Orders = Orders; }

Then Decorate your Controller class with the new attribute:

  [PopulateViewBag]
  public class ShippingManifestController : Controller

That's all there is to it! If you are populating ViewBags all over the place from your constructor, then you may consider creating a controller base class with the abstract method SetViewBagItems. Then you only need one ResultFilterAttribute class to do all the work.