强类型通用属性替代方案
Strongly Typed Generic Attribute alternatives
让我们从一个简单的视图模型开始:
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:/
另一种选择是将两者封装到一个 class 中,ModelBinder 可以在 post-back 上重新创建:
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 自动神奇地映射这些值,或者是否有创建强类型非通用属性的方法?
您可以将 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() 表达式以在不使用魔术字符串的情况下提取 属性 的名称。
要使 属性 名称成为强类型,您可以使用 C# 6 中引入的 nameof
。
nameof
是一个类似于 typeof
的表达式,但不是返回值的类型,而是 returns 作为字符串的值。
Used to obtain the simple (unqualified) string name of a variable, type, or member. When reporting errors in code, hooking up model-view-controller (MVC) links, firing property changed events, etc., you often want to capture the string name of a method. Using nameof helps keep your code valid when renaming definitions.
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; }
}
让我们从一个简单的视图模型开始:
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:/
另一种选择是将两者封装到一个 class 中,ModelBinder 可以在 post-back 上重新创建:
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 自动神奇地映射这些值,或者是否有创建强类型非通用属性的方法?
您可以将 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() 表达式以在不使用魔术字符串的情况下提取 属性 的名称。
要使 属性 名称成为强类型,您可以使用 C# 6 中引入的 nameof
。
nameof
是一个类似于 typeof
的表达式,但不是返回值的类型,而是 returns 作为字符串的值。
Used to obtain the simple (unqualified) string name of a variable, type, or member. When reporting errors in code, hooking up model-view-controller (MVC) links, firing property changed events, etc., you often want to capture the string name of a method. Using nameof helps keep your code valid when renaming definitions.
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; }
}