Reputation: 5451
I have a view model:
public class SelectVendorViewModel : IValidatableObject
{
[Display(Name = "Document Date")]
[RequiredUnless("IsPidDv")]
public DateTime? DocumentDate { get; set; }
[Display(Name = "Document Number")]
[RequiredUnless("IsPidDv")]
public int? DocumentNumber { get; set; }
[Display(Name = "Vendor")]
[RequiredUnless("IsPidDv")]
public Guid? VendorId { get; set; }
public List<SelectListItem> Vendors { get; set; }
[Display(Name="PID/DV")]
public bool IsPidDv { get; set; }
public Guid? SalesReportId { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return SelectVendorViewModelValidator.ValidateSalesReport(validationContext, this);
}
}
A custom model binder:
internal class SelectVendorViewModelBinder : DefaultModelBinder, IModelBinder<SelectVendorViewModel>
{
private readonly IVendorUnitOfWork _uow;
public SelectVendorViewModelBinder(IVendorUnitOfWork uow)
{
_uow = uow;
}
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = bindingContext.Model as SelectVendorViewModel;
if (model == null || !model.VendorId.HasValue || !model.DocumentDate.HasValue || !model.DocumentNumber.HasValue)
{
return;
}
var salesReport = _uow.SalesReportRepository.GetSalesReport(model.VendorId.Value, model.DocumentNumber.Value,
model.DocumentDate.Value);
if (salesReport != null)
{
model.SalesReportId = salesReport.Id;
}
}
}
And a validator:
internal class SelectVendorViewModelValidator
{
internal static IEnumerable<ValidationResult> ValidateSalesReport(ValidationContext validationContext, SelectVendorViewModel viewModel)
{
if (viewModel.IsPidDv)
{
yield break;
}
if (!viewModel.SalesReportId.HasValue || viewModel.SalesReportId.Value == default(Guid))
{
yield return new ValidationResult("Sales report document does not exist.");
}
}
}
And the controller action that is being posted to:
[HttpPost]
public virtual ActionResult SelectVendor(SelectVendorViewModel selectVendorVM)
{
selectVendorVM.Vendors = GetVendors();
if (!ModelState.IsValid)
{
return View(selectVendorVM);
}
return RedirectToAction(MVC.Licensing.Endorsements.Create(selectVendorVM.SalesReportId));
}
The binder is running correctly, I can step through it in the debugger. But the SelectVendorViewModel.Validate
method is never called. The property validation passes, and if I set a breakpoint in the controller action ModelState.IsValid
is true. I thought it might be something with the custom RequiredUnless
annotation, but even when I remove them the validation doesn't work. I use this same pattern in lots of places in this app but this is the only one that doesn't work. The only difference I could find between this and the others is the RequiredUnless
annotation and I was able to rule that out. What am I missing?
EDIT: Here's how I register the model binders:
Custom IModelBinderProvider
:
public class GenericModelBinder : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
var genericBinder = typeof(IModelBinder<>).MakeGenericType(modelType);
var binder = DependencyResolver.Current.GetService(genericBinder) as IModelBinder;
return binder;
}
}
In Global.asax Application_Start method:
ModelBinderProviders.BinderProviders.Add(new GenericModelBinder());
And in the Ninject config:
kernel.Bind<IModelBinder<SelectVendorViewModel>>().To<SelectVendorViewModelBinder>();
Upvotes: 1
Views: 2009
Reputation: 12132
Oh (cringe) you're not calling the base method of the ModelBinder
which in turn calls the Validate method on the model. ;)
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = bindingContext.Model as SelectVendorViewModel;
if (model == null || !model.VendorId.HasValue || !model.DocumentDate.HasValue || !model.DocumentNumber.HasValue)
{
return;
}
var salesReport = _uow.SalesReportRepository.GetSalesReport(model.VendorId.Value, model.DocumentNumber.Value,
model.DocumentDate.Value);
if (salesReport != null)
{
model.SalesReportId = salesReport.Id;
}
// this is important as we overrode but still need base
// functionality to effect a validate
base.OnModelUpdated(controllerContext, bindingContext);
}
Upvotes: 1