2015-05-13 4 views
9

Начнём с простой вид модели:Сильно типизированных Общие альтернативы Атрибут

public class MyViewModel 
{ 
    public string Value { get; set; } 
    public IEnumerable<SelectListItem> Values { get; set; } 
} 

выпадающий список может выглядеть следующим образом:

@Html.DropDownListFor(m => m.Value, Model.Values) 

Однако, поскольку выпадающий список требует двух значений она может «т быть использовано как:

public class MyViewModel 
{ 
    [UIHint("DropDownList")] 
    public string Value { get; set; } 
    public IEnumerable<SelectListItem> Values { get; set; } 
} 

с видом содержащий:

@Html.EditForModel() 

Потому что нет присущего способ выпадающий знать источник, пока вы не derrive от UIHint:

public DropDownListAttribute : UIHintAttribute 
{ 
    public DropDownListAttribute (string valuesSource) 
    : base("DropDownList", "MVC") 
    { 
    } 
} 

Тогда можно было бы использовать его как:

public class MyViewModel 
{ 
    [DropDownList("Planets")] 
    public string PlanetId{ get; set; } 
    public IEnumerable<SelectListItem> Planets { get; set; } 

    [DropDownList("Cars")] 
    public string CarId{ get; set; } 
    public IEnumerable<SelectListItem> Cars { get; set; } 
} 

Однако, это не очень строго типизировано, кто-то переименовывает один из magic strings или имена прав, не меняя другого, и он ломается во время выполнения.

Одно теоретическое решение создать общий атрибут:

public DropDownListAttribute<TModel, TValue> : UIHintAttribute 
{ 
    public DropDownListAttribute (Expression<Func<TModel, TValue> expression) 
    : base("DropDownList", "MVC") 
    { 
    } 
} 

и использование будет:

public class MyViewModel 
{ 
    [DropDownList<MyViewModel, IEnumerable<SelectListItem>>(m => m.Planets)] 
    public string PlanetId{ get; set; } 
    public IEnumerable<SelectListItem> Planets { get; set; } 
} 

But (currently) Generic Attributes aren't allowed:/

Другой вариант заключается в инкапсуляции два в один класс, который ModelBinder может воссоздать на обратной стороне:

public class DropDownListTemplate 
{ 
    public string SelectedValue { get; set; } 
    public IEnumerable<SelectListItem> Values { get; set; } 
} 

public class MyViewModel 
{ 
    public DropDownListTemplate Planet { get; set; } 
} 

Это создает простоту в шаблонах ViewModel, Binding и EditFor/DisplayFor, но из моих ограниченных знаний об AutoMapper это добавляет сложности, когда AutoMapper Mapping to Properties of Class Properties. Насколько я знаю, я не могу просто:

public class MyPlanets 
{ 
    public string SelectedPlanet { get; set; } 
} 

Mapper.CreateMap<MyPlanets, MyViewModel>(); 
Mapper.CreateMap<MyViewModel, MyPlanets>(); 

Есть ли более простой способ с automapper отобразить эти значения автоматически волшебно или есть способ создания сильно типизированных необщего атрибут?

+0

Даже если вы не использовали генерики, я не верю, что C# позволит вам использовать лямбда-выражения, украшая элементы с пользовательскими атрибутами. Это в значительной степени ограничено литералами и выражениями typeof(). – StriplingWarrior

+1

@ StriplingWarrior oh хорошая точка! Нет 'Expression',' Func' и т. Д. ... yeesh. Возможно, я могу [зависимость вводить выпадающие значения непосредственно в атрибут] (http://stackoverflow.com/questions/4695902/asp-net-mvc-3-action-filters-and-autofac-dependency-injection). .. :) –

+0

@ErikPhilips вам придется переопределить шаг активатора атрибутов, не так ли? Поскольку ваши конструкторы будут хотеть ценности, когда вы их украшаете. Возможно, вложение свойств внутри атрибута ... сложно. Вам все равно придется переопределить фазу построения атрибута. –

ответ

0

Вы можете назвать свою собственность в Planets как «PlanetSelectedValue», которая заставит AutoMapper автоматически определять, какое значение идет там. (т. е. Planet.SelectedValue отображает PlanetSelectedValue)

Это похоже на проблему с магической строкой, поскольку если вы измените имя свойства, AutoMapper больше не будет знать, где разместить это значение, но это ничем не отличается от любого другого AutoMapper вопрос.

public class MyPlanets 
{ 
    public string PlanetSelectedValue { get; set; } 
} 

ИЛИ:

Используйте AutoMapper в IgnoreMap приписывать игнорировать свойство DropDownListTemplate и сделать отдельный AutoMapper дружественный PlanetId свойство, которое манипулирует значения внутри DropdownListTemplate.

public class DropDownListTemplate 
{ 
    public string SelectedValue { get; set; } 
    public IEnumerable<SelectListItem> Values { get; set; } 
} 

public class MyViewModel 
{ 
    [IgnoreMap] 
    public DropDownListTemplate Planet { get; set; } 
    public string PlanetId 
    { 
     get { return Planet.SelectedValue; } 
     set { Planet.SelectedValue = value; } 
    } 
} 

Кроме того, когда это возможно для вашего проекта, посмотрите на выражение C# 6,0 nameof(), чтобы тянуть в имени свойства без использования волшебной строки.

1

Для того, чтобы имя свойства сильно типизированных можно использовать nameof, который вводится в C# 6.

nameof это выражение так же, как typeof, но вместо возвращения типа значения она возвращает значение в виде строки.

Используется для получения простого (неквалифицированного) строкового имени переменной, типа или члена. Когда вы сообщаете об ошибках в коде, подключаете ссылки на модель-просмотр-контроллер (MVC), изменяете события изменения и т. Д., Вы часто хотите записать имя строки метода. Использование nameof помогает сохранить код в действии при переименовании определений.

public class MyViewModel 
{ 
    [DropDownList(nameof(Planets))] 
    public string PlanetId{ get; set; } 
    public IEnumerable<SelectListItem> Planets { get; set; } 

    [DropDownList(nameof(Cars))] 
    public string CarId{ get; set; } 
    public IEnumerable<SelectListItem> Cars { get; set; } 
} 
+0

Я рассмотрю это. Очень многообещающий. –

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