views:

466

answers:

2

If you have a model-bound parameter in an action method, how can you get to that parameter in an action filter?

[MyActionFilter]
public ActionResult Edit(Car myCar)
{
    ...
}

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        //I want to access myCar here
    }

}

Is there anyway to get myCar without going through the Form variables?

+1  A: 

Not sure about OnActionExecuted but you can do it in OnActionExecuting:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // I want to access myCar here

        if(filterContext.ActionParameters.ContainsKey("myCar"))
        {
            var myCar = filterContext.ActionParameters["myCar"] as Car;

            if(myCar != null)
            {
                // You can access myCar here
            }
        }
    }
}
eu-ge-ne
I don't think you can use ActionFilter there, it is ActionFilterAttribute. Also where did you get the ActionParameters property on the ActionExecutedContext?
Yuriy Faktorovich
Thanks, it is ActionFilterAttribute - you are right. I have not noticed that the question is about OnActionExecuted.
eu-ge-ne
Thanks guys. Edited my original question to state ActionFilterAttribute.
Shlomo
A: 
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private Car _myCar;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if(filterContext.ActionParameters.ContainsKey("myCar"))
        {
            _myCar = filterContext.ActionParameters["myCar"] as Car;
        }

        // or the shorter less readable version:
        //_myCar = filterContext.ActionParameters
            //.SingleOrDefault(kvp => kvp.Key == "myCar").Value as Car;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // I want to access myCar here


        if(_myCar != null)
        {
            // You can access myCar here
        }

    }
}
Yuriy Faktorovich
Filter authors must *never* change instance state (e.g. writing to instance fields) within an action filter. Use filterContext.HttpContext.Items for this instead.
Levi
@Levi: if you're talking about HttpContext.Current.Items or filterContext.HttpContext, then the car isn't available at OnActionExecuted. Why shouldn't filters have instance variables?
Yuriy Faktorovich
@Yuriy: You can store objects in HttpContext.Items (filterContext.HttpContext.Items) in OnActionExecuting() and read them from OnActionExecuted(). The reason that filters must never contain mutable state is that the same instance of the filter might be serving multiple requests simultaneously. There's no guarantee that each request gets its own instance of a filter.
Levi
@Levi: What about filters that have properties. For example http://www.squaredroot.com/2008/04/02/MVC-Error-Handler-Filter/. By your logic, that wouldn't work if there are several of those passing different items to the property. Unless MVC is smart enough to know which was passed?
Yuriy Faktorovich
@Yuriy: This is a CLR thing, not an MVC thing. When the CLR reads attribute metadata from the assembly, it constructs the type and calls any property setters whose values are stored in the metadata. This fully-hydrated instance can be cached. For example, if there is a [MyActionFilter] on AController and a [MyActionFilter] on BController, the instance on AController might be shared across all AControllers, and the instance on BController might be shared across all BControllers. This is in fact how TypeDescriptor (and DataAnnotations validation, by extension) works.
Levi
@Levi Can you please show me where it says CLR caches these types?
Yuriy Faktorovich