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

时间:2015-07-18 15:25:11

标签: c# .net asp.net-mvc asp.net-mvc-4 razor

我有这样的界面:

public interface IFoo
{
    decimal Amount { get; set; }
}

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

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

我不想为Foo1Foo2中的每一个创建新视图。

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

@model IFoo

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

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

    @Html.ValidationMessageFor(x => x.Amount)
</div>

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

如果我为每种类型创建一个新视图,那么每一个都可以。

更新:我试图将界面更改为答案中提供的抽象类,但它既没有帮助。

2 个答案:

答案 0 :(得分:1)

不幸的是,您无法使用界面执行此操作。当您使用html帮助程序生成html时,它首先为属性生成ModelMetadata(对于强类型的html帮助程序,通过调用

ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

这会根据属性生成元数据,并考虑其属性。如果是TextBoxFor(),则会调用GetUnobtrusiveValidationAttributes() HtmlHelper方法来生成data-val-*属性。

这里的关键是它的属性的元数据,以及获取元数据的属性没有任何验证属性。对于你的评论&#34; @ Model.GetType()是Foo1或Foo2&#34; ,它不会尝试获取具体类型的实例并生成其元数据。

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

答案 1 :(得分:0)

我建议您改为使用抽象或只使用基类,在子类中定义具有一些默认范围和覆盖的Amount属性。

public abstract class Test
{
    protected int _number;
    [System.ComponentModel.DataAnnotations.Range(0, 10)]
    public abstract int NumProp
    {
        get;
        set;
    }
}


public class Test2 : Test
{
    [System.ComponentModel.DataAnnotations.Range(100, 1000)]
    public override int NumProp
    {
        get{
            return _number;
        }
        set
        {
            _number = value;
        }
    }
}