Set ViewBag from ActionFilterAttribute

The site I am creating custom colors that can be set by the user (only on certain pages). I want to grab that data within an ActionFilterAttribute and set it in the ViewBag so I can then get the data in my _Layout.cshtml.

Here is my ActionFilterAttribute...

public class PopulateColorOptionsAttribute : ActionFilterAttribute
{
    private readonly OptionsDataHelper optionsDataHelper;

    public PopulateOptionsAttribute(OptionsDataHelper optionsDataHelper)
    {
        this.optionsDataHelper = optionsDataHelper;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        await base.OnActionExecutionAsync(context, next);

        // Get the cemetery data and set it on the view bag.
        var personId = Convert.ToInt32(context.RouteData.Values["personId"]);
        context.Controller.ViewBag.OptionsData = await optionsDataHelper.GetValueAsync(personId, CancellationToken.None);
    }
}

Unfortunately, I receive an error on ViewBag that states:

'object' does not contain a definition for 'ViewBag' and no extension method 'ViewBag' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) [dnx451]

I'm pretty sure I'm not understanding something correctly about Filters and I would appreciate guidance on how to achieve what I am looking to do.

Answers


ActionExecutingContext.Controller is declared of type Object since the framework doesn't impose any restriction on which classes can be controllers.

If you are always creating your controllers inheriting from the base Controller class, then you can use that assumption in your filter and cast context.Controller as Controller:

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
    await base.OnActionExecutionAsync(context, next);

    var controller = context.Controller as Controller;
    if (controller == null) return;
    controller.ViewBag.Message = "Foo message";    
}

If you cannot make that assumption, then you can use a similar approach inspecting the result in the context:

public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
    var viewResult = context.Result as ViewResult; //Check also for PartialViewResult and ViewComponentResult
    if (viewResult == null) return;
    dynamic viewBag = new DynamicViewData(() => viewResult.ViewData);
    viewBag.Message = "Foo message";

    await base.OnResultExecutionAsync(context, next);
}

Cast the context.Controller as Controller

Try to change the following line

context.Controller.ViewBag.OptionsData = await optionsDataHelper.GetValueAsync(personId, CancellationToken.None);

to

((Controller)(context.Controller)).ViewBag.OptionsData = await optionsDataHelper.GetValueAsync(personId, CancellationToken.None);

Need Your Help

Browserify and ES6/ES2015 classes (babel compiler)

javascript ecmascript-6 browserify babeljs commonjs

I have 2 classes (ES2015 Classes style) in separate files and one App file with require. I want to use this CommonJS modules in browser. Most popular lib is Browserify with Babel compiler for ES2015

Runtime error using the Eclipse Abstract Syntax Tree

java eclipse-jdt

I'm trying to use AST parser in a non-plugin environment. The code compiles, but I get the following runtime error: