2014-07-11 3 views
0

Я создал пользовательский HtmlHelper для отображения адреса на веб-странице. Адрес должен быть динамическим в зависимости от страны, в которой находится адрес. Для этого у меня есть простой интерфейс с несколькими реализациями. Мой контроллер передает конкретную реализацию в представление, которое строго типизировано для интерфейса. При отладке в представлении отображается правильный тип реализации, и я передаю эту модель в свой пользовательский HtmlHelper в качестве типичного параметра типа. Этот пользовательский HtmlHelper принимает только модели типа IAddress или его реализации, но TModel всегда является IAddress независимо от того, какой тип передается в него. Это проблема из-за атрибутов отдельных реализаций, поскольку я использую существующие HtmlHelpers, такие как Html.EditorFor и Html.LabelFor в моем собственном помощнике, чтобы отображать поля и метки для адреса, включая правильную проверку при отправке.Выражение <Func <TModel, string >> где TModel - это интерфейс, тип никогда не передается.

Я считаю, что проблемы в том, что представление строго типизировано для IAddress, а HtmlHelper игнорирует фактический тип модели и подходит прямо к типу вида, как я могу обойти это?


интерфейса и исполнители Классы:

public interface IAddress 
{ 
    // Dumbed down to one property to save space 
    String City { get; set; } 
    String State { get; set; } 
} 

public class AddressUS : IAddress 
{ 
    // Required and displayed on page. 
    [DisplayName("City")] 
    [Required(ErrorMessage = "City is required!")] 
    public String City { get; set; } 

    // Required and displayed on page. 
    [DisplayName("State")] 
    [Required(ErrorMessage = "State is required!")] 
    public String State { get; set; } 
} 

public class AddressJP : IAddress 
{ 
    // Required and displayed on page. 
    [DisplayName("Prefecture")] 
    [Required(ErrorMessage = "Prefecture is required!")] 
    public String City { get; set; } 

    // Not required and not displayed on page. 
    [DisplayName("State")] 
    public String State { get; set; } 
} 
Действие


Контроллер:

public ActionResult DisplayAddress() 
{ 
    // Returning a specific type for testing. 
    AddressJP address = new AddressJP() { City = "Test" }; 
} 


Вид:

@* 
    Set to the interface to accept all implementations, but possibly also the cause of the issue. 
    Setting to AddressJP, but I need it dynamic. 
*@ 
@model IAddress 
@using Custom.HtmlExtensions 

<div id="address"> 
    @Html.AddressEditorForModel(true) 
</div> 


HtmlHelper:

public static MvcHtmlString AddressEditorForModel<TModel>(this HtmlHelper<TModel> helper, Boolean showLabels) where TModel : IAddress 
    { 
     // TModel is always IAddress unless I change the view to be a specific implementation. 

     StringBuilder sb = new StringBuilder(); 

     PropertyInfo pInfo = typeof(TModel).GetProperty("City"); 
     ParameterExpression paramExpr = Expression.Parameter(typeof(TModel)); 
     MemberExpression propertyAccess = Expression.MakeMemberAccess(paramExpr, pInfo); 
     var lambdaExpr = Expression.Lambda<Func<TModel, string>>(propertyAccess, paramExpr); 

     if (showLabels) 
      sb.AppendLine(helper.LabelFor(lambdaExpr).ToString()); 
     sb.AppendLine(helper.EditorFor(lambdaExpr).ToString()); 

     return MvcHtmlString.Create(sb.ToString()); 
    } 

ответ

1

Вы не должны использовать отражение, чтобы добраться до значения. Вы указали ограничение общего типа, поэтому .NET знает, что TModel должен быть IAddress.

Вы просто должны быть в состоянии сделать

var city = helper.ViewData.Model.City; 

В самом деле, вы должны быть в состоянии избавиться от родового метода в целом - просто использовать HtmlHelper<IAddress> в качестве параметра, и он должен работать нормально.

Кроме того, вы можете использовать helper.Label и т. Д. Вместо helper.LabelFor - они работают одинаково, но не требуют выражения, просто имя свойства.

+0

Использование helper.ViewData.Model.City прекрасно работает для получения значений этого свойства, но то, что мне нужно, это атрибуты этих свойств, которые будут использоваться. Например, DisplayName для метки и проверки в редакторе. Я не понимаю, как это могло бы это сделать? – WebDevNewbie

+0

@WebDevNewbie Не работает ли это просто при использовании 'helper.Label'? В любом случае, посмотрите на «ModelMetadataProviders.Current.GetMetadataForType» - он дает вам все эти атрибуты на основе типа автоматически, не нужно связываться с атрибутами самостоятельно. – Luaan

+0

helper.Label (helper.ViewData.Model.City) устанавливает значение метки в значение свойства City, а не значение атрибута в свойстве City. helper.LabelFor делает, но для этого требуется выражение, которое потребует TModel.Я буду экспериментировать с ModelMetadataProviders для немного здесь и посмотреть, где он идет :) – WebDevNewbie

Смежные вопросы