J'ai la définition de modèle de vue suivante
public class AccessRequestViewModel
{
public Request Request { get; private set; }
public SelectList Buildings { get; private set; }
public List<Person> Persons { get; private set; }
}
Donc, dans ma demande, il doit y avoir au moins 1 personne pour une demande d'accès. Quelle approche pourriez-vous utiliser pour valider? Je ne veux pas que cette validation se produise dans mon contrôleur, ce qui serait simple à faire. Le seul choix est-il un attribut de validation personnalisé?
Edit: Actuellement en cours de validation avec FluentValidation (Nice library!)
RuleFor(vm => vm.Persons)
.Must((vm, person) => person.Count > 0)
.WithMessage("At least one person is required");
Si vous utilisez des annotations de données pour effectuer la validation, vous pourriez avoir besoin d'un attribut personnalisé:
public class EnsureOneElementAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var list = value as IList;
if (list != null)
{
return list.Count > 0;
}
return false;
}
}
et alors:
[EnsureOneElement(ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }
ou pour le rendre plus générique:
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;
}
}
et alors:
[EnsureMinimumElements(1, ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }
Personnellement, j'utilise FluentValidation.NET au lieu d'annotations de données pour effectuer la validation car je préfère la logique de validation impérative au lieu de la déclaration. Je pense que c'est plus puissant. Donc, ma règle de validation ressemblerait simplement à ceci:
RuleFor(x => x.Persons)
.Must(x => x.Count > 0)
.WithMessage("At least a person is required");
Une autre façon possible de gérer les validations de comptage pour les membres de collection de l'objet modèle de vue consiste à avoir une propriété calculée renvoyant le comptage de la collection ou de la liste. Un RangeAttribute peut ensuite être appliqué comme dans le code ci-dessous pour appliquer la validation du nombre:
[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;
}
}
Dans le code ci-dessus, ItemCount est un exemple de propriété calculée sur un modèle de vue en cours de validation et Items est un exemple de propriété de collection de membres dont le nombre est en cours de vérification. Dans cet exemple, au moins un élément est appliqué sur le membre de la collection et la limite maximale est la valeur maximale qu'un entier peut prendre, ce qui, pour la plupart des applications pratiques, est illimité. Le message d'erreur en cas d'échec de validation peut également être défini via le membre ErrorMessage du RangeAttribute dans l'exemple ci-dessus.
La réponse de Darin est bonne mais la version ci-dessous vous donnera automatiquement un message d'erreur utile.
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}
Message d'erreur:
Le code suivant fonctionne dans asp.net core 1.1.
[Required, MinLength(1, ErrorMessage = "At least one item required in work order")]
public ICollection<WorkOrderItem> Items { get; set; }
Vous avez deux choix ici, soit créer un attribut de validation personnalisé et décorer la propriété avec, soit vous pouvez faire en sorte que votre ViewModel implémente l'interface IValidatableObject
(qui définit une méthode Validate
)
J'espère que cela t'aides :)
Une approche pourrait consister à utiliser un constructeur privé et une méthode statique pour renvoyer une instance de l'objet.
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; }
}
En utilisant toujours l'usine pour instancier votre ViewModel, vous pouvez vous assurer qu'il y aura toujours une personne.
Ce n'est probablement pas idéal pour ce que vous voulez, mais cela fonctionnerait probablement.
Il serait très propre et élégant d'avoir une validation personnalisée. Quelque chose comme ça:
public class AccessRequestViewModel
{
public Request Request { get; private set; }
public SelectList Buildings { get; private set; }
[AtLeastOneItem]
public List<Person> Persons { get; private set; }
}
Ou [MinimumItems(1)]
.