Я предполагаю, что вы хотите, чтобы ваши ViewModels автоматически вводили что-то через их конструктор - например, какой-то объект конфигурации, который View будет использовать для определения того, что показывать. Я также предполагаю, что этот подход вызывает ошибку «Нет конструктора без параметров для этого объекта», когда MVC пытается автоматически создать и связать экземпляр модели из аргументов вашего действия контроллера. Затем давайте также предположим, что мы будем использовать инфраструктуру DI для автоматической вставки объекта SiteConfig в наши контроллеры во время выполнения.
Это означает, что единственная проблема, которую нам нужно решить, - это как вставить внедренный объект из нашего Контроллера в ViewModels его действий, когда они автоматически связаны.
Итак, давайте определим базовую модель, от которой будут наследоваться другие.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
А теперь давайте создадим модель, которая наследуется от нее.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
А теперь давайте определим базовый контроллер, от которого унаследуются наши контроллеры.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Теперь мы определим наш фактический контроллер.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Предполагая, что мы используем Ninject для DI, Ninject будет настроен на автоматическое создание контроллера и передачу конкретного объекта ISiteConfig
в его конструктор во время выполнения.
Теперь мы добавляем наше действие в контроллер.
Индекс Действие
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
И вот в этой точке MVC, если ничего не делать, взорвется с ошибкой «Constructorless Constructor», если вы попытаетесь вызвать Index Action, потому что MVC не может найти конструктор ViewModel, который не принимает аргументов.
И так, ответ. Нам нужно переопределить ModelBinder по умолчанию.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
И нам нужно установить это как связующее устройство модели по умолчанию в global.asax.cs
:
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
Вот и все. Как вы можете видеть, при просмотре действия «Индекс» сейчас MVC будет использовать нашу пользовательскую привязку модели. Он поймет, что IndexViewModel является производным от BaseViewModel, и поэтому попытается раскрутить экземпляр IndexViewModel с помощью ISiteConfig, который он может найти в контроллере действия (поскольку Controller наследуется от BaseController).