Best practice for using interface type as model in

2020-07-18 02:12发布

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.

2条回答
2楼-- · 2020-07-18 02:56

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;
        }
    }
}
查看更多
聊天终结者
3楼-- · 2020-07-18 02:59

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.

查看更多
登录 后发表回答