nurdyguy
nurdyguy

Reputation: 2945

ViewBag not passing from base controller

We have converted our app to be ASP.NET Core but I am having a problem with the ViewBag populating correctly. We have the following base controller

public abstract class ControllerBase : Controller
{
    public ControllerBase()
    {
        ViewBag.Title = "MySite";
    }
  ...// lots of other stuff here also
}

All of our other controllers inherit from ControllerBase but by the time we get to the view and use the following

@{
    ViewBag.Title = ViewBag.Title + " - " + Model.PageTitle;
}

But in the view the ViewBag.Title is null. It does not cause an error but we end up with just " - MyPage" rather than "MySite - MyPage" for the browser title.

This all worked correctly in the previous version of .net, just not working now in ASP.NET Core. As I step through the debugger I see that the ControllerBase constructor is being called but the ViewBag data is not persisting.

This leaves me with two questions:

  1. Is there something new/different in ASP.NET Core which changed the scope of the ViewBag?
  2. What is the best way to fix this? (Without adding the value in a bunch of places.)

Edit: I set a debugger stop on the first line of the base controller and stepped through with the ViewBag.Title set as my watch variable. As I step through I can see the value get set and then I move from the base controller to the constructor for the specific action controller. As I step through that constructor the ViewBag.Title is still set. As soon as I hit the first line of the Index() method the ViewBag.Title turns to null.

Edit2: Here is a simple foo project illustrating the issue https://github.com/nurdyguy/ViewBagIssue

Upvotes: 7

Views: 3374

Answers (1)

nurdyguy
nurdyguy

Reputation: 2945

I still don't know why this is happening but here is a workaround I found. Create an OnActionExecuting filter:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MyProj.Filters
{
    public class ViewBagFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // do something before the action executes
            var controller = context.Controller as Controller;
            controller.ViewBag.Title = "MyPage";
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do something after the action executes
        }
    }
}

and add the following to the ConfigureServices method in Startup.cs:

services.AddMvc(options =>
{
    options.Filters.Add(typeof(ViewBagFilter));
    ... // you may have more here...
});

You'll notice the var controller = context.Controller as Controller; has a cast because the controller object on context is an object but it is easy enough to fix.

I'll post back here if I ever find out exactly what caused the issue to begin with. Good hunting!

Upvotes: 8

Related Questions