dev*_*xer 14 c# asp.net-mvc attributes dry
这个问题的灵感来自我与ASP.NET MVC的斗争,但我认为它也适用于其他情况.
假设我有一个ORM生成的模型和两个ViewModel(一个用于"详细信息"视图,一个用于"编辑"视图):
模型
public class FooModel // ORM generated
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public int Age { get; set; }
public int CategoryId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
显示ViewModel
public class FooDisplayViewModel // use for "details" view
{
[DisplayName("ID Number")]
public int Id { get; set; }
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
编辑ViewModel
public class FooEditViewModel // use for "edit" view
{
[DisplayName("First Name")] // not DRY
public string FirstName { get; set; }
[DisplayName("Last Name")] // not DRY
public string LastName { get; set; }
[DisplayName("Email Address")] // not DRY
[DataType("EmailAddress")] // not DRY
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")] // not DRY
public SelectList Categories { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
请注意,ViewModel上的属性不是DRY - 重复了很多信息.现在假设这个场景乘以10或100,你可以看到它很快变得非常乏味且容易出错,以确保ViewModel之间的一致性(因此也可以跨视图).
我怎么能"干掉"这段代码?
在你回答之前,"只是把所有的属性都放在上面FooModel,"我试过了,但它没有用,因为我需要保持我的ViewModel"平坦".换句话说,我不能只用模型组合每个ViewModel - 我需要我的ViewModel只有View应该使用的属性(和属性),而View不能挖掘子属性得到价值观.
更新
LukLed的回答建议使用继承.这肯定减少了非DRY代码的数量,但它并没有消除它.请注意,在上面的示例中,DisplayName属性的Category属性需要写入两次,因为属性的数据类型在显示和编辑ViewModel之间是不同的.这在小规模上不是什么大问题,但随着项目的规模和复杂性的扩大(想象更多的属性,每个属性更多的属性,每个模型更多的视图),仍然有可能"重复自己"相当多.也许我在这里干得太远了,但我仍然宁愿让我所有的"友好名字",数据类型,验证规则等只输入一次.
声明BaseModel,继承并添加其他属性:
public class BaseFooViewModel
{
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
}
public class FooDisplayViewModel : BaseFooViewModel
{
[DisplayName("ID Number")]
public int Id { get; set; }
}
public class FooEditViewModel : BaseFooViewModel
Run Code Online (Sandbox Code Playgroud)
编辑
关于类别.不应该编辑视图模型public string CategoryName { get; set; }而public List<string> Categories { get; set; }不是SelectList?这样你就可以放在public string CategoryName { get; set; }基类中并保持DRY.编辑视图通过添加来增强类List<string>.
我假设你这样做是为了利用HtmlHelpers EditorFor和DisplayFor,并且不希望在整个应用程序中隆重地宣布同样的事情4000次.
干掉它的最简单方法是实现自己的ModelMetadataProvider.ModelMetadataProvider正在读取这些属性并将它们呈现给模板助手.MVC2已经提供了一个DataAnnotationsModelMetadataProvider实现来实现这一目标,因此继承它会让事情变得非常简单.
为了帮助您入门,这是一个将camelcased属性名称拆分为空格的简单示例,FirstName => First Name:
public class ConventionModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
HumanizePropertyNamesAsDisplayName(metadata);
if (metadata.DisplayName.ToUpper() == "ID")
metadata.DisplayName = "Id Number";
return metadata;
}
private void HumanizePropertyNamesAsDisplayName(ModelMetadata metadata)
{
metadata.DisplayName = HumanizeCamel((metadata.DisplayName ?? metadata.PropertyName));
}
public static string HumanizeCamel(string camelCasedString)
{
if (camelCasedString == null)
return "";
StringBuilder sb = new StringBuilder();
char last = char.MinValue;
foreach (char c in camelCasedString)
{
if (char.IsLower(last) && char.IsUpper(c))
{
sb.Append(' ');
}
sb.Append(c);
last = c;
}
return sb.ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
然后你需要做的就是注册它就像在Global.asax的Application Start中添加你自己的自定义ViewEngine或ControllerFactory:
ModelMetadataProviders.Current = new ConventionModelMetadataProvider();
Run Code Online (Sandbox Code Playgroud)
现在只是为了告诉你我不是在欺骗这是我用来获得相同HtmlHelper的视图模型.*.作为装饰ViewModel的体验:
public class FooDisplayViewModel // use for "details" view
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3150 次 |
| 最近记录: |