c# - 如何在模型和 View 模型中使用 "DRY up"C# 属性?

标签 c# asp.net-mvc attributes dry

这个问题的灵感来自于我与 ASP.NET MVC 的斗争,但我认为它也适用于其他情况。

假设我有一个 ORM 生成的模型和两个 ViewModel(一个用于“详细信息” View ,一个用于“编辑” View ):

型号

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; }
}

显示 View 模型

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; }
}

编辑 View 模型

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; }
}

请注意,ViewModel 上的属性不是 DRY——很多信息是重复的。现在想象一下这个场景乘以 10 或 100,您会发现它很快就会变得非常乏味并且容易出错,以确保跨 ViewModel(因此跨 View )的一致性。

我怎样才能“干掉”这段代码?

在您回答“只需将所有属性放在 FooModel 上”之前,我已经尝试过了,但它没有用,因为我需要让我的 ViewModel 保持“平坦”。换句话说,我不能只将每个 ViewModel 与一个 Model 组合起来——我需要我的 ViewModel 只具有 View 应该使用的属性(和属性),并且 View 不能深入到子属性中获得值(value)。

更新

LukLed 的回答建议使用继承。这肯定会减少非 DRY 代码的数量,但不会消除它。请注意,在我上面的示例中,Category 属性的 DisplayName 属性需要写入两次,因为显示和编辑 ViewModel 的属性数据类型不同.这在小规模上不会是什么大问题,但随着项目规模和复杂性的扩大(想象更多的属性,每个属性更多的属性,每个模型更多的 View ),仍然有可能“重复自己”相当多。或许我在这里使用 DRY 太过分了,但我仍然宁愿只输入一次我所有的“友好名称”、数据类型、验证规则等。

最佳答案

我假设您这样做是为了利用 HtmlHelpers EditorFor 和 DisplayFor,并且不希望在整个应用程序中隆重声明同一事物 4000 次。

干这件事的最简单方法是实现您自己的 ModelMetadataProvider。 ModelMetadataProvider 正在读取这些属性并将它们呈现给模板助手。 MVC2 已经提供了一个 DataAnnotationsModelMetadataProvider 实现来让事情继续进行,所以继承它让事情变得非常简单。

这里是一个简单的示例,它将驼峰属性名称拆分为空格,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();
    }
}

然后您所要做的就是注册它,就像在 Global.asax 的 Application Start 中添加您自己的自定义 ViewEngine 或 ControllerFactory 一样:

ModelMetadataProviders.Current = new ConventionModelMetadataProvider();

现在只是为了向您展示我没有作弊,这是我用来获得相同 HtmlHelper 的 View 模型。*。作为装饰 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; }
    }

关于c# - 如何在模型和 View 模型中使用 "DRY up"C# 属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2269144/

相关文章:

c# - 像 MVC4 Razor 模板

c# - 写入文件时 Json.net 异步

c# - 添加 MEF 后 MVC 应用程序崩溃

c# - IDENTITY_INSERT 设置为 OFF - Visual Studio

javascript - 我们如何允许在 Dynamics CRM 中对单个字段进行多项选择?

c# - 获取 knockout 模型中的 MVC Person 模型书籍列表(可观察数组)

c# - 使用 EPPlus 计算列中的单元格

javascript - jQuery 检查表单中所有必需的输入是否不为空

C# 覆盖子类中的属性

constructor - 使用私有(private)属性复制构造函数