【问题标题】:How do I remain DRY with asp.net mvc view models & data annotation attributes?如何使用 asp.net mvc 视图模型和数据注释属性保持 DRY?
【发布时间】:2011-05-21 15:00:56
【问题描述】:

如何使用 Asp.Net MVC 的 asp.net mvc 视图模型和数据注释(验证、显示和数据建模)属性保持 DRY?我已将模型对象以及特定于操作的视图模型传递给视图。我发现这两个方向都存在一些试图保持干燥的问题。

  • 使用模型对象作为您的视图模型:这在简单的情况下工作得很好,并且允许您在每个模型对象上只编写一次数据注释属性。当您有需要多个对象类型的复杂视图时,就会出现问题。生成的视图模型架构是使用视图模型类和实际模型类的混合体。此外,此方法可能会向您的视图公开您不希望的模型属性。

  • 对每个操作使用唯一的视图模型类: 视图模型类仅包含视图特定的属性,并使用数据注释属性进行修饰。根据我的经验,这种方法并没有被证明是非常干燥的,因为数据注释属性往往会在视图模型类中重复。例如,新视图模型和编辑视图模型共享很多(但不是全部)属性和数据注释。

如何使用 asp.net mvc 视图模型和数据注释属性保持 DRY?

【问题讨论】:

  • 不幸的是,没有完美的解决方案不会导致验证元数据一些重复。至少我没有找到。

标签: asp.net-mvc


【解决方案1】:

一个不错的选择是从 DataAnnotations 切换到 Fluent Validation。 它允许您将常见的验证逻辑封装在一个类中,您可以稍后将其应用于您的模型。

来自documentation

[Validator(typeof(PersonValidator))]
public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

public class PersonValidator : AbstractValidator<Person> {
    public PersonValidator() {
        RuleFor(x => x.Id).NotNull();
        RuleFor(x => x.Name).Length(0, 10);
        RuleFor(x => x.Email).EmailAddress();
        RuleFor(x => x.Age).InclusiveBetween(18, 60);
    }
}

【讨论】:

  • 这绝对看起来是一个有吸引力的选择,但对于这个特定问题并没有真正的帮助。除非我错过了什么
  • 答案旨在将一组验证器分组到一个类中,然后您可以将其应用于不同的 ViewModel。例如,您可以为 New 和 Edit ViewModels 声明一个具有公共验证器的类,如果您选择使用 ViewModels,不可避免地会产生一些重复,但我认为这是一个很好的权衡,主要是因为您给出的原因.你可以用这个来补充第一个答案。
【解决方案2】:

我在 c# 中定义了这样的元数据:

public class Meta
{
    public class Client
    {
        public class Name
        {
            public const bool Required = true;
            public const DataType Type = DataType.Text;
            public const int MaxLength = 30;
            public const int MinLength = 1;
            public const string Regex = @"^[\w\d\.-_]{1,30}$";
        }

        public class Email
        {
            public const bool Required = false;
            public const DataType Type = DataType.EmailAddress;
            public const int MaxLength = 256;
            public const int MinLength = 4;
            public const string Regex = @"^.+@.+$";
        }
    }
}

将它们声明为常量允许您在 BL 实体和 UI 模型上使用 DataAnnotations:

[DataContract]
[Serializable]
public class ClientInfo
{
    [DataMember]
    [Required(AllowEmptyStrings = !Meta.Client.Name.Required)]
    [StringLength(Meta.Client.Name.MaxLength, MinimumLength = Meta.Client.Name.MinLength)]
    [RegularExpression(Meta.Client.Name.Regex)]
    public string Name { get; set; }

    ...
}

嗯,是的,您复制了属性,但没有复制元数据!此外,我有一个简单的预处理器来从模板生成 sql 脚本(对 *.Required 等的特殊处理):

创建表 dbo.Client( 名称 nvarchar({#Client.Name.MaxLength}) {#Client.Name.Required}, 电子邮件 nvarchar({#Client.Email.MaxLength}) {#Client.Email.Required}, ....

在 UI 上,您可以使用继承来不重复属性。例如,如果您有一个具有 10 个属性的模型,但只需要编辑其中的 2 个,请创建 EditModel 并从中继承 ViewModel。这里的关键是将元数据放在一个存储中并尽可能多地使用它。希望你能明白。

【讨论】:

    【解决方案3】:

    到目前为止,我发现使用继承来组合共享属性效果最好。我对每个操作都使用了一个独特的视图类,并且对目前的解决方案非常满意。它不能解决 100% 的情况,但确实涵盖了大多数情况,并且几乎消除了重复的数据合约属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      • 2011-07-04
      相关资源
      最近更新 更多