Pergunta

I would like to isolate a controller action completely from other elements of the system, as it is a refactor to legacy code so that the action can be rendered in various places in the system. In order to isolate any overlap in property names in the partial view's model, I was hoping to prefix the form name attributes with a certain value and make use of the BindAttribute to instruct the ModelBinder to match up the model properties with the appropriately prefixed form fields.

Since I have a handful of actions that make use of this model, I was hoping I could place the [Bind] decorator directly on the model class itself, avoiding having to decorate every model parameter coming into each of those actions. However, this approach doesn't seem to register with the binder, so I end up with unpopulated properties. On the other hand, if I move the decorators to the parameters, all is well.

The documentation for BindAttribute indicates it can be used on classes. Is this not a supported scenario?

Foi útil?

Solução

You could write a custom model binder for the corresponding view model:

public class MyViewModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = "some_prefix";
        return base.BindModel(controllerContext, bindingContext);
    }
}

which you could register in your Application_Start and associate with your view model:

ModelBinders.Binders.Add(typeof(MyViewModel), new MyViewModelBinder());

In this example, I have hardcoded the prefix, but you could make the model binder a bit more generic and reusable and take into account the BindAttribute that you could use to decorate your view model with:

public class MyViewModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // TODO: cache the result of this LINQ query to 
        // avoid using reflecting on each request. After all
        // the metadata of the view model won't change at runtime
        var bindAttribute = bindingContext
            .ModelType
            .GetCustomAttributes(typeof(BindAttribute), true)
            .OfType<BindAttribute>()
            .FirstOrDefault();

        bindingContext.ModelName = bindAttribute != null ? bindAttribute.Prefix : null;
        return base.BindModel(controllerContext, bindingContext);
    }
}

and then all that's left is to decorate your view model with the Bind attribute:

[Bind(Prefix = "some_prefix")]
public class MyViewModel
{
    public string Foo { get; set; }
    public string Bar { get; set; }
}

and request the action that is taking this view model:

/someaction?some_prefix.foo=the_foo_value&some_prefix.bar=the_bar_value
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top