清单的ViewModel验证


76

我有以下视图模型定义

public class AccessRequestViewModel
{
    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    public List<Person> Persons { get; private set; }
}

因此,在我的应用程序中,必须至少有1个人才能进行访问请求。您可以使用哪种方法进行验证?我不希望这种验证在我的控制器中发生,这很容易做到。唯一选择是自定义验证属性吗?

编辑:当前正在执行此验证与FluentValidation(不错的库!)

RuleFor(vm => vm.Persons)
                .Must((vm, person) => person.Count > 0)
                .WithMessage("At least one person is required");

Answers:


172

如果使用数据注释执行验证,则可能需要一个自定义属性:

public class EnsureOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count > 0;
        }
        return false;
    }
}

然后:

[EnsureOneElement(ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }

或使其更通用:

public class EnsureMinimumElementsAttribute : ValidationAttribute
{
    private readonly int _minElements;
    public EnsureMinimumElementsAttribute(int minElements)
    {
        _minElements = minElements;
    }

    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count >= _minElements;
        }
        return false;
    }
}

然后:

[EnsureMinimumElements(1, ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }

我个人使用FluentValidation.NET代替数据注释来执行验证,因为我更喜欢命令式验证逻辑而不是声明式。我认为它更强大。因此,我的验证规则将如下所示:

RuleFor(x => x.Persons)
    .Must(x => x.Count > 0)
    .WithMessage("At least a person is required");

看来我需要使用重载Must()来使用person.Count,请查看我的编辑,如果您使用的版本更友好,请告诉我:)
ryan

1
@ryan,确实在文档中显示了此方法的两个重载。所以我的版本更友好。如果Visual Studio强调它为错误,请不要担心。如果您尝试编译,它应该可以工作。仅仅是VS Intellisense不够先进,无法理解它:-)因此RuleFor(x => x.Persons).Must(x => x.Count > 0).WithMessage("At least a person is required");可以编译和正常工作。
Darin Dimitrov'3

奇怪,现在没有强调。谢谢!
ryan

如果您使用的任何验证框架只是好奇:谢谢达林,你选择了一个很酷的属性名称(客户端以及内置。数据注解提供客户端的优势,如果一个发现有用的。
萨罗Taşciyan

我应该如何在剃须刀中使用它?如果我编写@foreach(Model.Person中的var p){...} @ Html.ValidationMessageFor(model => Model.Person),则调用验证服务器方法,但未显示验证消息。
凯特

18

处理视图模型对象的集合成员的计数验证的另一种可能方法是,使计算所得的属性返回集合或列表的计数。然后可以像下面的代码中那样应用RangeAttribute来执行计数验证:

[Range(minimum: 1, maximum: Int32.MaxValue, ErrorMessage = "At least one item needs to be selected")]
public int ItemCount
{
    get
    {
        return Items != null ? Items.Length : 0;
    }
}

在上面的代码中,ItemCount是正在验证的视图模型上的示例计算属性,而Items是正在检查其计数的示例成员集合属性。在此示例中,对集合成员强制执行了至少一项,并且最大限制是整数可以采用的最大值,对于大多数实际目的,该最大值是无界的。在上面的示例中,也可以通过RangeAttribute的ErrorMessage成员设置有关验证失败的错误消息。


14

以下代码在asp.net core 1.1中有效。

[Required, MinLength(1, ErrorMessage = "At least one item required in work order")]
public ICollection<WorkOrderItem> Items { get; set; }

1
看来,这不再是.NET的核心2.1.0(预览版)的作品
斯文

3
这在.Net Core 2.2中有效,我已经在List类型上进行了尝试。如果您想为[Required]属性使用单独的自定义消息,也可以这样做。[Required(ErrorMessage =“ Missing Meters”),MinLength(1,ErrorMessage =“需要至少1米”)] public List <Meter> Meters {get; 组; }
Jeshwel '19

8

Darin的回答很好,但是下面的版本会自动为您提供有用的错误消息。

public class MinimumElementsAttribute : ValidationAttribute
{
    private readonly int minElements;

    public MinimumElementsAttribute(int minElements)
    {
        this.minElements = minElements;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var list = value as IList;

        var result = list?.Count >= minElements;

        return result
            ? ValidationResult.Success
            : new ValidationResult($"{validationContext.DisplayName} requires at least {minElements} element" + (minElements > 1 ? "s" : string.Empty));
    }
}

用法:

[MinimumElements(1)]
public List<Customer> Customers {get;set}

[MinimumElements(2)]
public List<Address> Addresses {get;set}

错误信息:

  • 客户需要至少1个元素
  • 地址至少需要2个元素

2

您在这里有两个选择,要么创建“自定义验证属性”并用它来装饰属性,要么可以使ViewModel实现IValidatableObject接口(定义Validate方法)。

希望这可以帮助 :)


0

进行自定义验证会非常干净优雅。像这样:

public class AccessRequestViewModel
{
    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    [AtLeastOneItem]
    public List<Person> Persons { get; private set; }
}

或者[MinimumItems(1)]


0

一种方法可能是使用私有构造函数和静态方法来返回对象的实例。

public class AccessRequestViewModel
{
    private AccessRequesetViewModel() { };

    public static GetAccessRequestViewModel (List<Person> persons)
    {
            return new AccessRequestViewModel()
            {
                Persons = persons,
            };
    }

    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    public List<Person> Persons { get; private set; }
}

通过始终使用工厂实例化ViewModel,可以确保始终有一个人。

这可能不是您想要的理想选择,但可能会起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.