Search code examples
c#asp.net-coremodel-view-controllerserviceaction-filter

pass value returned from a method to action filter


I have _serviceOne injected into my controller that has a method which returns an int value. I'm trying to pass this value into my custom action filter.

This isn't working and I am getting the error: An object reference is required for the non-static field, method, or property 'NameController._serviceOne' where I try to set the Number = _serviceOne.GetIntNumber.

I am aware that I can access a value if it is inside the controller (eg: controller parameter, ViewBag, ViewData, a variable in the controller), but I want to pass the value to the CustomActionFilter filter's Number property.

The filter and service method work the way I want it to, but it won't let me pass the value from _serviceOne.GetIntNumber into the filter. Why is this not working, and how could I make it work?

NameController.cs:

public class NameController : Controller
{
    private readonly ServiceOne _serviceOne;

    public NameController(ServiceOne serviceOne)
    {
        _serviceOne = serviceOne;
    }

    [CustomActionFilter(Name = "CorrectName", Number = _serviceOne.GetIntNumber)] //does not work
    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }
}

CustomActionFilter.cs:

public class CustomActionFilter : ActionFilterAttribute
{
    public string Name { get; set; }
    public int Number { get; set; }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (Name == "CorrectName" && Number == 1) {
            RouteValueDictionary routeDictionary = new RouteValueDictionary { { "action", "SomeAction" }, { "controller", "NameController" } };
            context.Result = new RedirectToRouteResult(routeDictionary);
        }

        base.OnActionExecuted(context);
    }
}

Solution

  • Attributes are created at compile time so it's not possible to pass them run-time values in the constructor.

    Instead, you can access NameController's instance of the service, like this:

    public class NameController : Controller
    {
        private readonly ServiceOne _serviceOne;
    
        public ServiceOne ServiceOne => _serviceOne;
    
        public NameController(ServiceOne serviceOne)
        {
            _serviceOne = serviceOne;
        }
    }
    
    public class CustomActionFilter : ActionFilterAttribute
    {
        public string Name { get; set; }
        public int Number { get; set; }
    
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            var controller = context.Controller as NameController;
            var service = controller.ServiceOne;
            //Use the service here
        }
    }
    

    See also Access a controller's property from an action filter