22

I have a generic action filter, and i want to get current model in the OnActionExecuting method. My current implementation is like below:

public class CommandFilter<T> : IActionFilter where T : class, new()
{
    public void OnActionExecuting(ActionExecutingContext actionContext)
    {
        var model= (T)actionContext.ActionArguments["model"];
    }
}

It works well if my all model names are same. But i want to use differnet model names.

How to solve this problem?

Edit

public class HomeController : Controller
{
    [ServiceFilter(typeof(CommandActionFilter<CreateInput>))]
    public IActionResult Create([FromBody]CreateInput model)
    {
        return new OkResult();
    }
}
johnny 5
  • 19,893
  • 50
  • 121
  • 195
adem caglin
  • 22,700
  • 10
  • 58
  • 78

3 Answers3

20

ActionExecutingContext.ActionArguments is just a dictionary,

    /// <summary>
    /// Gets the arguments to pass when invoking the action. Keys are parameter names.
    /// </summary>
    public virtual IDictionary<string, object> ActionArguments { get; }

And you need to loop through it if you need to avoid hardcoded parameter name ("model"). From the same SO answer for asp.net:

When we create a generic action filter that needs to work on a class of similar objects for some specific requirements, we could have our models implement an interface => know which argument is the model we need to work on and we can call the methods though the interface.

In your case you may write something like this:

public void OnActionExecuting(ActionExecutingContext actionContext)
{
    foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is T))
    {
         T model = argument as T;
         // your logic
    }
}
Yahya Hussein
  • 8,767
  • 15
  • 58
  • 114
Set
  • 47,577
  • 22
  • 132
  • 150
10

You can use ActionExecutingContext.Controller property

    /// <summary>
    /// Gets the controller instance containing the action.
    /// </summary>
    public virtual object Controller { get; }

and converting result to base MVC Controller get access to model:

((Controller)actionExecutingContext.Controller).ViewData.Model
Set
  • 47,577
  • 22
  • 132
  • 150
  • it throws a cast exception : Unable to cast object of type 'MyNamespace.HomeController' to type 'Microsoft.AspNetCore.Mvc.Controller'. – adem caglin Aug 11 '16 at 13:41
  • oo sorry, i have forgotten `Controller` base definition. I edited code but now model is null(maybe i miss anything). see my update. – adem caglin Aug 11 '16 at 13:52
  • now I see - you want to get request model, not view model. I have posted another answer – Set Aug 11 '16 at 14:12
2

In case your controller action has multiple arguments and in your filter you want to select the one that is bound via [FromBody], then you can use reflection to do the following:

public void OnActionExecuting(ActionExecutingContext context)
{
    foreach (ControllerParameterDescriptor param in context.ActionDescriptor.Parameters) {
        if (param.ParameterInfo.CustomAttributes.Any(
            attr => attr.AttributeType == typeof(FromBodyAttribute))
        ) {             
            var entity = context.ActionArguments[param.Name];

            // do something with your entity...
Community
  • 1
  • 1
Felix K.
  • 14,171
  • 9
  • 58
  • 72
  • Will I get `given key 'model' was not found in dictionary` if my model was not deserialized correctly? – Azimuth Feb 18 '20 at 14:58
  • I am lacking the CustomAttributes method in ParameterInfo. Is there any other way to access the custom attributes? – Martin May 20 '22 at 14:13