首页 > 解决方案 > 如何在 ASP.NET 中干净地共享数据模型、ViewModel 和 DTO 的自定义验证器 (ValidationAttribute)

问题描述

我正在做一个 ASP.NET MVC 课程。我正在使用 ASP.NET WebAPI 2 构建一个 REST Web API。该应用程序还包含标准的 MVC 5 视图。我正在使用 DTO(数据传输对象)将 API 与数据模型分离。我已经创建了一个自定义 ValidationAttribute,我已将其应用于我的数据模型中的属性,并且我想对我的 DTO 上的属性以及在 MVC 视图中使用的属性 ViewModel 使用相同的 Validation 属性。

这需要将其ValidationContext.ObjectInstance转换为正确的类型。我找到了一个简单的解决方案,但我觉得它不是很优雅,我想知道是否有更好的方法来做到这一点。

我正在谈论的特定 ValidationAttribute 和属性:

[Min18YearsIfAMember]
public DateTime? DateOfBirth { get; set; }

在解决方案的上下文中(为简洁起见,删除了一些细节,包括 CustomerViewModel):

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public MembershipType MembershipType { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember]
    public DateTime? DateOfBirth { get; set; }
}    


public class CustomerDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember]
    public DateTime? DateOfBirth { get; set; }  
}

public class Min18YearsIfAMemberAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // Check it here
        var customer = validationContext.ObjectInstance as Customer;
        if (customer != null)
            return DoValidation(customer.MembershipTypeId, customer.DateOfBirth);

        // Check it here
        var customerVm = validationContext.ObjectInstance as CustomerViewModel;
        if (customerVm  != null)
            return DoValidation(customerVm.MembershipTypeId, customerVm.DateOfBirth);

        // Yes I should probably check it here too
        var customerDto = validationContext.ObjectInstance as CustomerDto;
            return DoValidation(customerDto.MembershipTypeId, customerDto.DateOfBirth);
    }

    private ValidationResult DoValidation( int membershipTypeId, DateTime? DateOfBirth)
    { 
        // Do the validation....
    }
}

它是可读的,但我觉得像这样检查每个可能的情况很难看ValidationContext.ObjectInstance as Customer

有没有更好的办法?

标签: c#asp.netasp.net-mvcmodel-bindingvalidationattribute

解决方案


在数据注释属性中,您可以在附加属性时指定依赖属性,并使用它可以验证对象类型的属性:

public class Min18YearsIfAMemberAttribute : ValidationAttribute
{
    private string _dependentProperty { get; set; }

    public Min18YearsIfAMemberAttribute(string dependentProperty)
    {
        this._dependentProperty = dependentProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var field = validationContext.ObjectType.GetProperty(_dependentProperty);
        if (field != null)
        {
            var dependentValue = (byte)field.GetValue(validationContext.ObjectInstance, null);
            
            return DoValidation(dependentValue, (DateTime?)value);
        }
        else
        {
            return new ValidationResult("<Your message here>");
        }
    }

    private ValidationResult DoValidation( int membershipTypeId, DateTime? DateOfBirth)
    { 
        // Do the validation....
    }

现在在附加属性时,我们指定依赖属性名称[Min18YearsIfAMember("MembershipTypeId")

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public MembershipType MembershipType { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember(nameof(MembershipTypeId))]
    public DateTime? DateOfBirth { get; set; }
}  

推荐阅读