Tim
Tim

Reputation: 3038

Binding during ActionFilterAttribute

Struggling to figure out why I am getting null returned from BindModel here. I have an attribute that is extending ActionFilterAttribute...

public class MyCachedAttribute : ActionFilterAttribute
{
  private IModelBinder binder = new DefaultModelBinder();
  private Type model;

  public MyCachedAttribute(Type model)
  {
     this.model = model;
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
     ModelBindingContext bindingContext = new ModelBindingContext()
     {
        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, model),
        ModelName = model.Name,
        ModelState = filterContext.Controller.ViewData.ModelState,
        ValueProvider = filterContext.Controller.ValueProvider
     };

     object data = binder.BindModel(filterContext.Controller.ControllerContext, bindingContext);

The data at this point is null.

Edit: I've come back to this and realised ModelState is empty (so causing the null) because the method has no model passed in normally (hence why I am binding in this circumstance, to pick it up).

  [MyCached(typeof(FooViewModel))]
  public ActionResult Foo()
  {
     return PartialView(new FooViewModel());
  }

How can I generate the ModelState for the type I have, and pass it into the binder? I am trying to avoid adding the model as an input parameter, since it causes problems, but its looking like I may have to sort those problems instead if this continues being an issue.

Thanks.

Edit2: I am using an ActionFilterAttribute here to modify the model sent as response in some circumstances, and in other circumstances it accepts a model to update in the cache. In this instance I need to bind it.

Upvotes: 0

Views: 1243

Answers (1)

Jani Hyytiäinen
Jani Hyytiäinen

Reputation: 5407

You should do model binding in modelbinder. Not ActionFilter. ActionFilters are for intercepting and modifying requests and responses. So, clean up your ActionResult like this.

public ActionResult Foo(FooViewModel model)
{
    return PartialView(model);
}

Then create a custom model binder.

public class CachedModelBinder : DefaultModelBinder {

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
    object data = base.BindModel(controllerContext, bindingContext);
    // data is not null anymore. You can do your custom stuff now, then return the model
    return data;
}

And register it in in Application_Start() in your global.asax.cs

ModelBinders.Binders.Add(typeof(FooViewModel), new CachedModelBinder());

Upvotes: 2

Related Questions