3

У меня есть интерфейс модели объявлена ​​с классом, реализующим его:ASP.NET MVC - метаданные используются с моделью от типа модели объекта вместо заявленной модели View

public interface IMyModel 
{ 
    [Range(1, 1000)] 
    [Display(Name = "ModelProp From Interface")] 
    int MyIntProperty { get; set; } 
    IMySubModel SubModel { get; } 
} 

public interface IMySubModel 
{ 
    [Range(1,1000)] 
    [Display(Name = "SubModelProp From Interface")] 
    int MyIntSubProperty { get; set; } 
} 

У меня также есть реализация модели, с различными Метаданные:

public class MyModelImplementation:IMyModel 
{ 
    [Display(Name = "ModelProp From Class")] 
    [Range(1, 15)] 
    public int MyIntProperty 
    { 
     get; 
     set; 
    } 
    public IMySubModel SubModel { get; set; } 
    public MyModelImplementation() 
    { 
     SubModel = new MySubModelImplementation(); 
    } 
} 

public class MySubModelImplementation: IMySubModel 
{ 
    [Display(Name = "SubModelProp From Class")] 
    [Range(1, 15)] 
    public int MyIntSubProperty 
    { 
     get; 
     set; 
    } 
} 

у меня есть вид, где я использую эту модель интерфейса:

@model MvcApplicationModelInterface.Models.IMyModel 
@using (Html.BeginForm()) 
{ 
    <p>Using Lambda Expression:</p> 

    @Html.DisplayNameFor(m=>m.MyIntProperty) 
    @Html.EditorFor(m=>m.MyIntProperty) 
    @Html.ValidationMessageFor(m=>m.MyIntProperty) 
    <br/> 
    @Html.DisplayNameFor(m=>m.SubModel.MyIntSubProperty) 
    @Html.EditorFor(m=>m.SubModel.MyIntSubProperty) 
    @Html.ValidationMessageFor(m=>m.SubModel.MyIntSubProperty) 
    <br/> 

    <p>Using String Expression:</p> 

    @Html.DisplayName("MyIntProperty") 
    @Html.Editor("MyIntProperty") 
    @Html.ValidationMessage("MyIntProperty") 
    <br/> 
    @Html.DisplayName("SubModel.MyIntSubProperty") 
    @Html.Editor("SubModel.MyIntSubProperty") 
    @Html.ValidationMessage("SubModel.MyIntSubProperty") 
    <br/> 
    @Html.ValidationSummary(true) 
    <br/> 
    <input type="submit" name="btnSubmit" value="btnSubmit" /> 
} 

An d У меня есть контроллер, который делает правильное связывание и инициализации модели:

[HttpGet] 
    public ActionResult Index() 
    { 
     ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application."; 
     var model = new MyModelImplementation(); 
     model.MyIntProperty = 111; 
     model.SubModel.MyIntSubProperty = 222; 
     return View(model); 
    } 

    [ActionName("Index"), HttpPost] 
    public ActionResult Save([ModelBinder(typeof(MyModelBinder))]IMyModel model) 
    { 
     return View(model); 
    } 

    public class MyModelBinder : DefaultModelBinder 
    { 
     protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
     { 
      return new MyModelImplementation(); 
     } 
    } 

Проблема:

Если возвращает действие контроллера вид с NOT NULL модели (возвращение View (новый MyModelImplementation() { ...});), результаты я вижу на экране, являются:

enter image description here

Однако, если возвращает действие результатов контроллера с нулевой модели (возвращение View (нуль);), то результаты:

enter image description here

Как вы можете видеть, поведение для хелмеров Lambda html и помощников строковых выражений различно, и ни одно из них не является согласованным вообще, и какое-то поведение выглядит как ошибка:

  • (ожидаемый) Для лямбда-хелперов отображаемое_имя берется из IMyModel
  • (неожиданный) Для выражения Струнный все метаданные взяты из MyModelImplementation класса (Другими словами, если вы используете Html.DisplayNameFor (м = > m.MyIntProperty) - он показывает метаданные из интерфейса (тип модели Declared in view), однако, если вы используете Html.DisplayName («MyIntProperty»), он использует метаданные model.GetType()).
  • (неожиданно) ВСЕ. Правила и последовательности проверки ВСЕГДА выбраны из метаданных MyModelImplementation (model.GetType()) вместо объявленного типа модели (IMyModel).
  • (ожидаемый) Для собственности строкового выражения помощников модели берется из интерфейса только в случае, если модель передается в представление является NULL
  • (неожиданный) Для метаданных свойства строкового выражения помощников подмодели является не возобновлено/уважаться на все , в случае, если модель передается зрения NULL

Вопрос:

Каков наилучший способ решения этой ASP.NET MVC Bug/Feature? Как заставить Html-расширения всегда использовать метаданные из типа модели Объявлено в представлении по умолчанию? Я попытался использовать MetadataTypeAttribute, но в этом случае тот, кто реализует модель, получит свободу перезаписывать оригинальные метаданные, указанные в интерфейсе, которые я не хочу разрешать.Поэтому я скорее ищу какую-то пользовательскую реализацию ModelMetadataProvider. Также некоторые атрибуты метаданных не соблюдаются в случае MetadataType, например RequiredAttribute.

ответ

0

Я не считаю, что разница между Editor() и EditorFor() является ошибкой. Я думаю, это связано с тем, что EditorFor() имеет доступ к информации о типе сильно типизированной модели, тогда как редактор должен использовать отражение, чтобы получить тип, который возвращает фактический тип, а не используемый тип интерфейса.

Я думаю, что вы видите, что строго типизированные версии имеют больший контекст и могут получить информацию об интерфейсе, а не просто позвонить по номеру GetType().

Проверка правильной работы, потому что вы сказали связующему устройству модели привязываться к MyModelBinder, который привязывается к MyModelImplementation. Проверка основана на методе, который вы отправляете, и на аргументах, которые он принимает.

+0

это неправда. Как редактор, так и редактор. Используйте ViewData.Metadata и ExpressionHelper для получения метаданных, поэтому они оба имеют доступ к объекту ContainerType объекта Metadata, указывающему на интерфейс. Но почему-то вместо ContainerType используется model.GetType() –

+0

@Philipp - Нет, оба используют 'ViewData.ModelMetadata', но способ, которым он получает метаданные, отличается. Они оба попадают туда через объект TemplateHelper, а TemplateHeler вызывает 'ModelMetadata.FromLambdaExpression' для строго типизированных и' ModelMetadata.FromStringExpression' для типов строк. Версия Lambda ищет тип контейнера на основе выражения, в то время как в строковой версии нет этой информации, поэтому нужно искать контейнер, используя 'GetType()'. В любом случае это не ошибка, потому что версии строк просто не имеют доступа к данным выражения. –

+0

Это утверждение также неточно: как вы видите, если вы передаете Model = null - для выражения строки он извлекает метаданные из IModel.Property, однако он не делает то же самое для ISubModel.SubProperty (Model.SubModel.SubProperty). Поэтому по крайней мере поведение непоследовательно. –