Best practice for using interface type as model in View and use real type attributes and validations

北战南征 提交于 2019-12-21 17:43:24

问题


I have such interface:

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

And I have some view models implement it:

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

I don't want to create a new view for each of Foo1 and Foo2.

So, I have created a view which has IFoo type model.

@model IFoo

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

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

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

But, it doesn't create client-side unobtrusive attributes like Range attribute in client-side.

If I create a new view for each of this types, then everyting will be ok.

Update: I have tried to change interface to abstract class as offered in the answer, but it didn't help neither.


回答1:


Unfortunately you cannot do this using an interface. When you use a html helper to generate html, it first generates the ModelMetadata for the property (in the case of the strongly typed html helpers, by calling

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

This generates the metadata based on the property taking into account its attributes. In the case of TextBoxFor(), the GetUnobtrusiveValidationAttributes() method of HtmlHelper is then called to generate the data-val-* attributes.

The key here is that its the metadata for the property, and the property its getting the metadata for does not have any validation attributes. And further to your comment "@Model.GetType() is Foo1 or Foo2", it does not attempt to get an instance of the concrete type and generate its metadata.

Unless you were to create your own ModelMetadataProvider and override the CreateMetadata() method, you need to create separate views for each concrete class.




回答2:


I would suggest you instead use an abstract or just use a base class, define the Amount property with some default range and override in the subclass.

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


来源:https://stackoverflow.com/questions/31492648/best-practice-for-using-interface-type-as-model-in-view-and-use-real-type-attrib

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!