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

本文关键字:验证 属性 最佳 类型 接口类型 View 模型 真实 | 更新日期: 2023-09-27 18:03:17

我有这样的接口:

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属性。

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

更新:我已经尝试改变接口抽象类提供的答案,但它没有帮助。

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

遗憾的是,使用接口无法做到这一点。当您使用html helper生成html时,它首先为属性生成ModelMetadata(在强类型html helper的情况下,通过调用

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

这会基于属性生成元数据,并考虑到它的属性。在TextBoxFor()的情况下,然后调用HtmlHelperGetUnobtrusiveValidationAttributes()方法来生成data-val-*属性。

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

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

我建议您使用抽象或仅使用基类,用一些默认范围定义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;
        }
    }
}