Istnieje sposób to zrobić bez konieczności kod szablonowe na górze każdej akcji kontrolera.
Musisz wymienić spoiwo domyślnego modelu z jednym z własną rękę:
protected void Application_Start()
{
// ...
ModelBinderProviders.BinderProviders.Clear();
ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider());
// ...
}
Operator modelu spoiwo wygląda następująco:
public class CustomModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
return new CustomModelBinder();
}
}
Teraz stworzenie własnego modelu spoiwo, które faktycznie wymusza walidację. Tutaj wykonano ciężki lifting:
public class CustomModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
ForceModelValidation(bindingContext);
}
private static void ForceModelValidation(ModelBindingContext bindingContext)
{
var model = bindingContext.Model as IValidatableObject;
if (model == null) return;
var modelState = bindingContext.ModelState;
var errors = model.Validate(new ValidationContext(model, null, null));
foreach (var error in errors)
{
foreach (var memberName in error.MemberNames)
{
// Only add errors that haven't already been added.
// (This can happen if the model's Validate(...) method is called more than once, which will happen when
// there are no property-level validation failures.)
var memberNameClone = memberName;
var idx = modelState.Keys.IndexOf(k => k == memberNameClone);
if (idx < 0) continue;
if (modelState.Values.ToArray()[idx].Errors.Any()) continue;
modelState.AddModelError(memberName, error.ErrorMessage);
}
}
}
}
Potrzebna jest również metoda rozszerzenia IndexOf. To jest tania implementacja, ale zadziała:
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
var i = 0;
foreach (var item in source)
{
if (predicate(item)) return i;
i++;
}
return -1;
}
Szczerze, zaczynam lubić to domyślne zachowanie. W przypadku sprawdzania poprawności na poziomie biznesowym w metodzie sprawdzania poprawności, która wiąże się z kosztownymi materiałami, takimi jak połączenia z bazami danych, lepiej jest je wywoływać, chyba że model jest prawidłowy. – Graham