让我们从一个简单的视图模型开始:
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()
因为没有固有的方法让下拉知道源,直到你从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 :(得分:1)
要强制键入属性名称,可以使用C#6中引入的nameof
。
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; }
}
答案 1 :(得分:0)
您可以将Planets中的属性命名为“PlanetSelectedValue”,这会导致AutoMapper自动确定其中的值。 (即Planet.SelectedValue映射到PlanetSelectedValue)
这类似于魔术字符串问题,如果您更改属性名称,AutoMapper将不再知道该值的放置位置,但这与任何其他AutoMapper问题没有什么不同。
public class MyPlanets
{
public string PlanetSelectedValue { get; set; }
}
OR:
使用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()表达式,以便在不使用魔术字符串的情况下提取属性名称。