在视图中使用接口类型作为模型并使用真实类型属性和验证的最佳实践

Far*_*yev 5 .net c# asp.net-mvc razor asp.net-mvc-4

我有这样的界面:

public interface IFoo
{
    decimal Amount { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我有一些视图模型实现它:

public class Foo1 : IFoo
{
    [Display(Name = "Foo1 Amount")]
    [Range(6, 11)]
    public decimal Amount { get; set; }
}

public class Foo2 : IFoo
{       
    [Display(Name = "Foo2 Amount")]
    [Range(1, 5)]
    public decimal Amount { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我不想为每个Foo1和 都创建一个新视图Foo2

所以,我创建了一个具有类型模型的视图IFoo

@model IFoo

<div>
    @Html.LabelFor(x => x.Amount)

    @Html.TextBoxFor(x => x.Amount)

    @Html.ValidationMessageFor(x => x.Amount)
</div>
Run Code Online (Sandbox Code Playgroud)

但是,它不会创建客户端不显眼的属性,例如Range客户端中的属性。

如果我为每种类型创建一个新视图,那么一切都会好起来的。

更新:我尝试将接口更改为答案中提供的抽象类,但它也没有帮助。

小智 1

不幸的是,您无法使用界面来做到这一点。当您使用 html 帮助程序生成 html 时,它首先ModelMetadata为属性生成 (在强类型 html 帮助程序的情况下,通过调用

ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
Run Code Online (Sandbox Code Playgroud)

这会根据属性并考虑其属性来生成元数据。在 的情况下TextBoxFor(),然后调用GetUnobtrusiveValidationAttributes()的方法HtmlHelper来生成data-val-*属性。

这里的关键是它的元数据属性并且它获取元数据的属性没有任何验证属性。根据您的评论“@Model.GetType() is Foo1 or Foo2”,它不会尝试获取具体类型的实例并生成其元数据。

除非您要创建自己的 ModelMetadataProvider 并重写 CreateMetadata() 方法,否则您需要为每个具体类创建单独的视图。