列表的 ViewModel 验证

我有以下视图模型定义

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

因此,在我的应用程序中,访问请求必须至少有一个人。您可以使用什么方法来验证?我不希望这种验证发生在我的控制器,这将是简单的做法。唯一的选择是自定义验证属性吗?

编辑: 目前使用 FluentValentication (很好的库!)执行这个验证

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

If you are using Data Annotations to perform validation you might need a custom attribute:

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

and then:

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

or to make it more generic:

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

and then:

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

Personally I use FluentValidation.NET instead of Data Annotations to perform validation because I prefer the imperative validation logic instead of the declarative. I think it is more powerful. So my validation rule would simply look like this:

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

One approach could be to use a private constructor and a static method to return an instance of the object.

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

By always using the factory to instantiate your ViewModel, you can ensure that there will always be a person.

This probably isn't ideal for what you want, but it would likely work.

It would be very clean and elegant to have a custom validation. Something like this:

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

Or [MinimumItems(1)].

You have two choices here, either create a Custom Validation Attribute and decorate the property with it, or you can make your ViewModel implement the IValidatableObject interface (which defines a Validate method)

Hope this helps :)

Another possible way to handle the count validations for view model object's collection members, is to have a calculated property returning the collection or list count. A RangeAttribute can then be applied like in the code below to enforce count validation:

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

In the code above, ItemCount is an example calculated property on a view model being validated, and Items is an example member collection property whose count is being checked. In this example, at least one item is enforced on the collection member and the maximum limit is the maximum value an integer can take, which is, for most of the practical purposes, unbounded. The error message on validation failure can also be set through the RangeAttribute's ErrorMessage member in the example above.

Following code works in asp.net core 1.1.

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

Darin's answer is good but the version below will automatically give you a useful error message.

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

Usage:

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


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

Error message:

  • Customers requires at least 1 element
  • Addresses requires at least 2 elements