Я выполнял некоторые эксперименты с ASP.NET MVC2 и столкнулся с интересной проблемой.
Я хотел бы определить интерфейс вокруг объектов, которые будут использоваться в качестве Моделей в приложении MVC. Кроме того, я хотел бы использовать в своих интересах новый DataAnnotation функционально путем повышения членов этого интерфейса с атрибутами проверки.
Так, если мой сайт будет иметь "фото" объект, то я определю следующий интерфейс:
public interface IPhoto
{
[Required]
string Name { get; set; }
[Required]
string Path { get; set; }
}
И я определю следующую реализацию:
public class PhotoImpl : IPhoto
{
public string Name { get; set; }
public string Path { get; set; }
}
Мой контроллер Приложения MVC мог бы включать методы как:
public class PhotoController : Controller
{
[HttpGet]
public ActionResult CreatePhoto()
{
return View();
}
[HttpPost]
public ActionResult CreatePhoto(IPhoto photo)
{
if(ModelState.IsValid)
{
return View();
}
else
{
return View(photo);
}
}
}
И наконец, для привязки PhotoImpls с параметрами в этих методах действия, я мог бы реализовать следующие расширения DefaultModelBinder:
public class PhotoModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if(bindingContext.ModelType == typeof(IPhoto))
{
IPhoto photo = new PhotoImpl();
// snip: set properties of photo to bound values
return photo;
}
return base.BindModel(controllerContext, bindingContext);
}
}
Все появляется к тому, чтобы работать отлично, за исключением того, что ModelState. Свойство IsValid в моем контроллере, кажется, не замечает недопустимые значения (скажите, пустой указатель) в свойствах [Required] реализации iPhoto.
Я подозреваю, что забыл устанавливать некоторую важную часть состояния в моей реализации ModelBinder. Какие-либо подсказки?
После проверки источника для System.Web.mvc.defaultModelBinder, похоже, это может быть решено с помощью немного другого подхода. Если мы полагаемся более активнее на базовую реализацию BindModel, похоже, что мы можем построить объект PhotoImpl, пока все еще потянув атрибуты проверки из iPhoto.
Что-то вроде:
public class PhotoModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(IPhoto))
{
ModelBindingContext newBindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
() => new PhotoImpl(), // construct a PhotoImpl object,
typeof(IPhoto) // using the IPhoto metadata
),
ModelState = bindingContext.ModelState,
ValueProvider = bindingContext.ValueProvider
};
// call the default model binder this new binding context
return base.BindModel(controllerContext, newBindingContext);
}
else
{
return base.BindModel(controllerContext, bindingContext);
}
}
}
Пробовали ли вы разместить атрибут [Обязательный] в своей модели и повторно протестировать? Может возникнуть проблема с применением атрибута к интерфейсу.
У меня была такая же проблема. Ответ: вместо переопределения BindModel() в вашей пользовательской связке моделей, переопределите CreateModel()...
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
{
if (modelType == typeof(IPhoto))
{
IPhoto photo = new PhotoImpl();
// snip: set properties of photo to bound values
return photo;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
Затем вы можете позволить базовому классу BindModel делать свои дела с валидацией :-)
.