Jak korzystać z IValidatableObject?
Rozumiem, że IValidatableObject
jest używany do walidacji obiektu w sposób, który porównuje właściwości ze sobą.
Nadal chciałbym mieć atrybuty do walidacji poszczególnych właściwości, ale w niektórych przypadkach chcę ignorować błędy niektórych właściwości.
Czy próbuję użyć go nieprawidłowo w poniższym przypadku? Jeśli nie, to jak to zaimplementować?
public class ValidateMe : IValidatableObject
{
[Required]
public bool Enable { get; set; }
[Range(1, 5)]
public int Prop1 { get; set; }
[Range(1, 5)]
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!this.Enable)
{
/* Return valid result here.
* I don't care if Prop1 and Prop2 are out of range
* if the whole object is not "enabled"
*/
}
else
{
/* Check if Prop1 and Prop2 meet their range requirements here
* and return accordingly.
*/
}
}
}
6 answers
Po pierwsze, dzięki @ paper1337 za wskazanie mi odpowiednich zasobów...Nie jestem zarejestrowany, więc nie mogę na niego zagłosować, proszę to zrobić, jeśli ktoś jeszcze to przeczyta.
Oto, jak osiągnąć to, co próbowałem zrobić.Klasa Walidacyjna:
public class ValidateMe : IValidatableObject
{
[Required]
public bool Enable { get; set; }
[Range(1, 5)]
public int Prop1 { get; set; }
[Range(1, 5)]
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (this.Enable)
{
Validator.TryValidateProperty(this.Prop1,
new ValidationContext(this, null, null) { MemberName = "Prop1" },
results);
Validator.TryValidateProperty(this.Prop2,
new ValidationContext(this, null, null) { MemberName = "Prop2" },
results);
// some other random test
if (this.Prop1 > this.Prop2)
{
results.Add(new ValidationResult("Prop1 must be larger than Prop2"));
}
}
return results;
}
}
Użycie Validator.TryValidateProperty()
doda do zbioru wyników, jeśli nie zostaną przeprowadzone walidacje. Jeśli nie ma nieudanej walidacji, nic nie zostanie dodane do kolekcji wyników, która jest oznaką sukcesu.
Doing the Walidacja:
public void DoValidation()
{
var toValidate = new ValidateMe()
{
Enable = true,
Prop1 = 1,
Prop2 = 2
};
bool validateAllProperties = false;
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(
toValidate,
new ValidationContext(toValidate, null, null),
results,
validateAllProperties);
}
Ważne jest, aby ustawić validateAllProperties
Na false, aby ta metoda działała. Gdy validateAllProperties
jest false, sprawdzane są tylko właściwości z atrybutem [Required]
. Pozwala to metodzie IValidatableObject.Validate()
obsługiwać walidacje warunkowe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-06 17:49:02
Cytat z bloga Jeffa Handleya na temat walidacji obiektów i właściwości za pomocą walidatora :
Podczas walidacji obiektu, następujący proces jest stosowany w Walidator.ValidateObject:
- Walidacja atrybutów na poziomie właściwości
- Jeśli walidatory są nieprawidłowe, przerwij walidację zwracając awarie
- sprawdzanie atrybutów na poziomie obiektu
- jeśli walidatory są nieprawidłowe, przerwij walidację zwracając awarie
- Jeśli na pulpicie framework i obiekt implementuje IValidatableObject, a następnie wywołać jego Validate method and return any failure(s)
Oznacza to, że to, co próbujesz zrobić, nie zadziała po wyjęciu z pudełka, ponieważ Walidacja zostanie przerwana w Kroku # 2. Możesz spróbować utworzyć atrybuty dziedziczące po wbudowanych i dokładnie sprawdzić obecność włączonej właściwości (poprzez interfejs) przed wykonując ich normalną walidację. Alternatywnie, możesz umieścić całą logikę walidacji encji w metodzie Validate
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-03 20:44:45
Aby dodać kilka punktów:
Ponieważ podpis metody Validate()
zwraca IEnumerable<>
, że yield return
może być używany do leniwego generowania wyników - jest to korzystne, jeśli niektóre kontrole walidacji są obciążone IO lub CPU.
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this.Enable)
{
// ...
if (this.Prop1 > this.Prop2)
{
yield return new ValidationResult("Prop1 must be larger than Prop2");
}
Ponadto, jeśli używasz MVC ModelState
, możesz przekonwertować błędy wyniku walidacji na wpisy ModelState
w następujący sposób (może to być przydatne, jeśli wykonujesz walidację w segregatorze niestandardowego modelu ):
var resultsGroupedByMembers = validationResults
.SelectMany(vr => vr.MemberNames
.Select(mn => new { MemberName = mn ?? "",
Error = vr.ErrorMessage }))
.GroupBy(x => x.MemberName);
foreach (var member in resultsGroupedByMembers)
{
ModelState.AddModelError(
member.Key,
string.Join(". ", member.Select(m => m.Error)));
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:12
Zaimplementowałem klasy abstrakcyjne ogólnego użycia dla walidacji
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace App.Abstractions
{
[Serializable]
abstract public class AEntity
{
public int Id { get; set; }
public IEnumerable<ValidationResult> Validate()
{
var vResults = new List<ValidationResult>();
var vc = new ValidationContext(
instance: this,
serviceProvider: null,
items: null);
var isValid = Validator.TryValidateObject(
instance: vc.ObjectInstance,
validationContext: vc,
validationResults: vResults,
validateAllProperties: true);
/*
if (true)
{
yield return new ValidationResult("Custom Validation","A Property Name string (optional)");
}
*/
if (!isValid)
{
foreach (var validationResult in vResults)
{
yield return validationResult;
}
}
yield break;
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-20 23:25:23
Problem z zaakceptowaną odpowiedzią polega na tym, że od wywołującego zależy, czy obiekt zostanie poprawnie zweryfikowany. Chciałbym albo usunąć RangeAttribute i zrobić walidację zakresu wewnątrz metody Validate lub chciałbym utworzyć niestandardowy atrybut podklasowania RangeAttribute, który bierze nazwę wymaganej właściwości jako argument konstruktora.
Na przykład:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class RangeIfTrueAttribute : RangeAttribute
{
private readonly string _NameOfBoolProp;
public RangeIfTrueAttribute(string nameOfBoolProp, int min, int max) : base(min, max)
{
_NameOfBoolProp = nameOfBoolProp;
}
public RangeIfTrueAttribute(string nameOfBoolProp, double min, double max) : base(min, max)
{
_NameOfBoolProp = nameOfBoolProp;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_NameOfBoolProp);
if (property == null)
return new ValidationResult($"{_NameOfBoolProp} not found");
var boolVal = property.GetValue(validationContext.ObjectInstance, null);
if (boolVal == null || boolVal.GetType() != typeof(bool))
return new ValidationResult($"{_NameOfBoolProp} not boolean");
if ((bool)boolVal)
{
return base.IsValid(value, validationContext);
}
return null;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-28 22:10:04
Podobała mi się odpowiedź cocogzy z wyjątkiem tej bazy połączeń.IsValid wywołał wyjątek przepełnienia stosu, ponieważ ponownie wprowadzał metodę IsValid. Więc zmodyfikowałem go do określonego rodzaju walidacji, w moim przypadku było to dla adresu e-mail.
[AttributeUsage(AttributeTargets.Property)]
class ValidEmailAddressIfTrueAttribute : ValidationAttribute
{
private readonly string _nameOfBoolProp;
public ValidEmailAddressIfTrueAttribute(string nameOfBoolProp)
{
_nameOfBoolProp = nameOfBoolProp;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext == null)
{
return null;
}
var property = validationContext.ObjectType.GetProperty(_nameOfBoolProp);
if (property == null)
{
return new ValidationResult($"{_nameOfBoolProp} not found");
}
var boolVal = property.GetValue(validationContext.ObjectInstance, null);
if (boolVal == null || boolVal.GetType() != typeof(bool))
{
return new ValidationResult($"{_nameOfBoolProp} not boolean");
}
if ((bool)boolVal)
{
var attribute = new EmailAddressAttribute {ErrorMessage = $"{value} is not a valid e-mail address."};
return attribute.GetValidationResult(value, validationContext);
}
return null;
}
}
To działa znacznie lepiej! Nie zawiesza się i generuje ładny komunikat o błędzie. Mam nadzieję, że to komuś pomoże!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-30 17:55:53