Jak używać adnotacji danych do warunkowej walidacji modelu?
Na przykład, powiedzmy, że mamy następujący model (osoba i starszy):
public class Person
{
[Required(ErrorMessage = "*")]
public string Name
{
get;
set;
}
public bool IsSenior
{
get;
set;
}
public Senior Senior
{
get;
set;
}
}
public class Senior
{
[Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
public string Description
{
get;
set;
}
}
I następujący widok:
<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>
<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>
<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>
Chciałbym być warunkowym wymaganym polem właściwości "Senior.Description" na podstawie wyboru właściwości "IsSenior" (true -> wymagane). Jak zaimplementować walidację warunkową w ASP.NET MVC 2 z adnotacjami danych?
c#
asp.net-mvc
forms
validation
Peter Stegnar
źródło
źródło
Senior
Obiekt zawsze jest starszy, więc dlaczego IsSenior mogą być fałszywe w tym przypadku. Czy nie potrzebujesz tylko, aby właściwość „Person.Senior” miała wartość null, gdyPerson.IsSenior
jest fałszywa. Albo dlaczego nie realizowaćIsSenior
własności w następujący sposób:bool IsSenior { get { return this.Senior != null; } }
.Odpowiedzi:
Istnieje znacznie lepszy sposób dodawania reguł walidacji warunkowej w MVC3; niech twój model odziedziczy
IValidatableObject
i zaimplementujValidate
metodę:public class Person : IValidatableObject { public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (IsSenior && string.IsNullOrEmpty(Senior.Description)) yield return new ValidationResult("Description must be supplied."); } }
Przeczytaj więcej w artykule Wprowadzenie do ASP.NET MVC 3 (wersja zapoznawcza 1) .
źródło
ModelState.IsValid
- nie nazywając Weryfikuj bezpośrednioRozwiązałem to obsługując słownik „ModelState” , który jest zawarty w kontrolerze. Słownik ModelState zawiera wszystkie elementy członkowskie, które muszą zostać zweryfikowane.
Oto rozwiązanie:
Jeśli potrzebujesz zaimplementować walidację warunkową na podstawie jakiegoś pola (np. Jeśli A = prawda, wtedy B jest wymagane), zachowując komunikaty o błędach na poziomie właściwości (nie dotyczy to niestandardowych walidatorów, które są na poziomie obiektu), możesz to osiągnąć obsługując „ModelState”, po prostu usuwając z niego niechciane walidacje.
... w jakiejś klasie ...
public bool PropertyThatRequiredAnotherFieldToBeFilled { get; set; } [Required(ErrorMessage = "*")] public string DepentedProperty { get; set; }
... lekcja trwa ...
... W jakiejś akcji kontrolera ...
if (!PropertyThatRequiredAnotherFieldToBeFilled) { this.ModelState.Remove("DepentedProperty"); }
...
Dzięki temu uzyskujemy walidację warunkową, pozostawiając wszystko inne bez zmian.
AKTUALIZACJA:
Oto moja ostateczna implementacja: użyłem interfejsu w modelu i atrybutu akcji, który weryfikuje model, który implementuje wspomniany interfejs. Interfejs przepisuje metodę Validate (ModelStateDictionary modelState). Atrybut w akcji po prostu wywołuje Validate (modelState) w IValidatorSomething.
Nie chciałem komplikować tej odpowiedzi, więc nie wspomniałem o ostatecznych szczegółach implementacji (które na końcu mają znaczenie w kodzie produkcyjnym).
źródło
Miałem wczoraj ten sam problem, ale zrobiłem to w bardzo czysty sposób, który działa zarówno po stronie klienta, jak i po stronie serwera.
Warunek: na podstawie wartości innej właściwości w modelu chcesz ustawić inną właściwość jako wymaganą. Oto kod
public class RequiredIfAttribute : RequiredAttribute { private String PropertyName { get; set; } private Object DesiredValue { get; set; } public RequiredIfAttribute(String propertyName, Object desiredvalue) { PropertyName = propertyName; DesiredValue = desiredvalue; } protected override ValidationResult IsValid(object value, ValidationContext context) { Object instance = context.ObjectInstance; Type type = instance.GetType(); Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null); if (proprtyvalue.ToString() == DesiredValue.ToString()) { ValidationResult result = base.IsValid(value, context); return result; } return ValidationResult.Success; } }
Tutaj PropertyName jest właściwością, dla której chcesz ustawić swój warunek DesiredValue to konkretna wartość PropertyName (właściwość), dla której Twoja druga właściwość musi zostać zweryfikowana pod kątem wymaganej
Powiedz, że masz następujące rzeczy
public class User { public UserType UserType { get; set; } [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))] public string Password { get; set; } }
Na koniec zarejestruj adapter dla swojego atrybutu, aby mógł przeprowadzić walidację po stronie klienta (umieściłem go w global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
źródło
Używam tego niesamowitego obiektu nuget, który wykonuje dynamiczne adnotacje ExpressiveAnnotations
Możesz zweryfikować dowolną logikę, o jakiej marzysz:
public string Email { get; set; } public string Phone { get; set; } [RequiredIf("Email != null")] [RequiredIf("Phone != null")] [AssertThat("AgreeToContact == true")] public bool? AgreeToContact { get; set; }
źródło
Możesz wyłączyć walidatory warunkowo, usuwając błędy z ModelState:
ModelState["DependentProperty"].Errors.Clear();
źródło
Dzięki Merritt :)
Właśnie zaktualizowałem to do MVC 3 na wypadek, gdyby ktoś uznało to za przydatne: Walidacja warunkowa w ASP.NET MVC 3 .
źródło
Obecnie istnieje platforma, która wykonuje tę warunkową walidację (wśród innych przydatnych walidacji adnotacji danych) po wyjęciu z pudełka: http://foolproof.codeplex.com/
W szczególności przyjrzyj się walidatorowi [RequiredIfTrue ("IsSenior")]. Umieszczasz to bezpośrednio na właściwości, którą chcesz zweryfikować, dzięki czemu uzyskujesz pożądane zachowanie błędu walidacji powiązanego z właściwością „Senior”.
Jest dostępny jako pakiet NuGet.
źródło
Musisz walidować na poziomie osoby, a nie na poziomie Senior, lub Senior musi mieć odniesienie do osoby nadrzędnej. Wydaje mi się, że potrzebujesz mechanizmu samooceny, który definiuje walidację na osobie, a nie na jednej z jej właściwości. Nie jestem pewien, ale wydaje mi się, że DataAnnotations nie obsługuje tego po wyjęciu z pudełka. To, co możesz zrobić, stwórz swój własny
Attribute
, pochodzący zValidationAttribute
tego, może być ozdobiony na poziomie klasy, a następnie utwórz niestandardowy walidator, który również pozwoli na działanie walidatorów na poziomie klasy.Wiem, że Validation Application Block obsługuje samoocenę zaraz po wyjęciu z pudełka, ale VAB ma dość stromą krzywą uczenia się. Niemniej jednak, oto przykład użycia VAB:
[HasSelfValidation] public class Person { public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } [SelfValidation] public void ValidateRange(ValidationResults results) { if (this.IsSenior && this.Senior != null && string.IsNullOrEmpty(this.Senior.Description)) { results.AddResult(new ValidationResult( "A senior description is required", this, "", "", null)); } } }
źródło
Miałem ten sam problem, potrzebowałem modyfikacji atrybutu [Wymagane] - make pole wymagane w zależności od żądania http. Rozwiązanie było podobne do odpowiedzi Dana Hunexa, ale jego rozwiązanie nie działało poprawnie (patrz komentarze). Nie używam dyskretnej weryfikacji, po prostu MicrosoftMvcValidation.js po wyjęciu z pudełka. Tutaj jest. Zaimplementuj swój atrybut niestandardowy:
public class RequiredIfAttribute : RequiredAttribute { public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/) { } protected override ValidationResult IsValid(object value, ValidationContext context) { //You can put your logic here return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need } }
Następnie musisz zaimplementować niestandardowego dostawcę, aby używać go jako adaptera w pliku global.asax
public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute> { ControllerContext ccontext; public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) : base(metadata, context, attribute) { ccontext = context;// I need only http request } //override it for custom client-side validation public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { //here you can customize it as you want ModelClientValidationRule rule = new ModelClientValidationRule() { ErrorMessage = ErrorMessage, //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required" ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none"; }; return new ModelClientValidationRule[] { rule }; } }
I zmodyfikuj swój global.asax linią
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));
i oto jest
[RequiredIf] public string NomenclatureId { get; set; }
Główną zaletą dla mnie jest to, że nie muszę kodować niestandardowego walidatora klienta, jak w przypadku dyskretnej weryfikacji. działa tak samo, jak [Wymagane], ale tylko w przypadkach, w których chcesz.
źródło
DataAnnotationsModelValidator
była dokładnie tym, co chciałem zobaczyć. Dziękuję Ci.Sprawdź walidację warunkową Simona Ince w MVC .
Pracuję teraz nad jego przykładowym projektem.
źródło
Typowe zastosowanie do warunkowego usuwania błędu ze stanu modelu:
Przykład:
public ActionResult MyAction(MyViewModel vm) { // perform conditional test // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey") // Do typical model state validation, inside following if: // if (!ModelState.IsValid) // Do rest of logic (e.g. fetching, saving
W twoim przykładzie zachowaj wszystko tak, jak jest i dodaj logikę sugerowaną do akcji kontrolera. Zakładam, że Twój ViewModel przekazany do akcji kontrolera ma obiekty Person i Senior Person z danymi wypełnionymi w nich z interfejsu użytkownika.
źródło
Używam MVC 5, ale możesz spróbować czegoś takiego:
public DateTime JobStart { get; set; } [AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")] [DisplayName("Start Date")] [Required] public DateTime? StartDate { get; set; }
W twoim przypadku powiedziałbyś coś w rodzaju „IsSenior == true”. Następnie wystarczy sprawdzić walidację akcji wysyłania.
źródło