直接在模型类上使用BindAttribute前缀

时间:2014-05-08 21:35:11

标签: .net asp.net-mvc-5 model-binding

我想将控制器操作与系统的其他元素完全隔离,因为它是遗留代码的重构,因此可以在系统的各个位置呈现操作。为了隔离局部视图模型中属性名称的任何重叠,我希望在表单名称属性前加上一定的值,并使用BindAttribute来指示ModelBinder将模型属性与适当的前缀表单字段。

由于我有一些使用这个模型的动作,我希望我可以将[Bind]装饰器直接放在模型类本身上,避免必须装饰进入每个动作的每个模型参数。但是,这种方法似乎没有注册到绑定器,所以我最终得到了无人居住的属性。另一方面,如果我将装饰器移动到参数,一切都很好。

BindAttribute的文档表明它可以在课程中使用。这不是受支持的情况吗?

1 个答案:

答案 0 :(得分:5)

您可以为相应的视图模型编写自定义模型绑定器:

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

您可以在Application_Start中注册并与您的视图模型关联:

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

在这个例子中,我已经硬编码了前缀,但你可以使模型绑定器更通用和可重用,并考虑可用于装饰视图模型的BindAttribute

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);
    }
}

然后剩下的就是使用Bind属性装饰您的视图模型:

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

并请求采用此视图模型的操作:

/someaction?some_prefix.foo=the_foo_value&some_prefix.bar=the_bar_value