Внедрение в конструктор экземпляра модели представления, используемого в качестве параметра метода Action - PullRequest
7 голосов
/ 01 января 2012

Когда модель представления создана, вы можете заполнить параметры (например, используемые в раскрывающемся списке) в свойстве сеттера модели представления. Проблема заключается в том, что когда эта модель представления позднее передается как параметр (с помощью платформы!) В метод действия, эти значения свойств не становятся автоматически повторного заполнения, поэтому, если вам необходимо повторно отобразить форму из-за ошибок проверки, вам необходимо снова заполнить эти параметры.

Одно потенциальное решение, которое я специально спрашиваю в этом вопросе, заключается в том, как заставить инфраструктуру MVC создать экземпляр модели представления с помощью инжектора конструктора, что обеспечит конструктор модели представления реализацией некоторого объекта доступа к данным ( например, хранилище), которое можно использовать для извлечения параметров, когда они запрашиваются представлением (например, в вспомогательном методе "DropDownListFor")?

Я думаю, что решение может иметь какое-то отношение к реализациям IModelBinderProvider или IModelBinder, но после экспериментов с этими вещами из примеров фрагментов кода здесь и там в сети, я все еще ищу полностью рабочий пример с загружаемым исполняемым кодом без какого-либо пропущенного кусочка того, как собрать все вещи вместе.

Если вы ищете альтернативную дискуссию о том, как заполнить список выбора, например, с помощью «Dependecy Lookup» вместо «Dependecy Injection» вы можете проверить следующее обсуждение: Лучший способ заполнить SelectList для ViewModel на GET / POST Лучший способ заполнить SelectList для ViewModel в GET / POST

Несколько дней назад я написал следующий дополнительный вопрос в этой теме о «инъекции зависимости», которую я сейчас ищу в этой теме: https://stackoverflow.com/a/8674525/310457 (который предоставляет пример кода о проблеме, решение которой я ищу)

Но вместо того, чтобы надеяться, что кто-то найдет эту старую ветку с менее конкретным названием, я создал новый вопрос с более конкретной темой о том, что я ищу. И я также предоставлю ссылку из этой ветки на этот новый вопрос для всех, кто хочет продолжить работу в отношении этого конкретного решения, которое я ищу.

1 Ответ

8 голосов
/ 11 июня 2014

Я предполагаю, что вы хотите, чтобы ваши 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).

...