JasCav
JasCav

Reputation: 34632

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.

Upvotes: 8

Views: 7450

Answers (2)

ShahMansoor
ShahMansoor

Reputation: 31

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);

Upvotes: 1

Daniel J.G.
Daniel J.G.

Reputation: 34992

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

Upvotes: 22

Related Questions