Один пользовательский связыватель модели для CreateModel, а другой для BindModel? - PullRequest
1 голос
/ 18 февраля 2012

Справочная информация: В моих методах обратной реакции MVC я получаю команду объекты вместо просмотра моделей. Идея состоит в том, что эти объекты команд (которые примерно соответствуют сценариям транзакций) будут настроены и готовы к выполнению при входе в метод действия, причем механизм связывания модели имеет заданные параметры, которые используются в процессе выполнения:

public class MyCommand : IMyCommand
{
    // In this case Value1 and Value2 are being set by the default model binder from posted form values - wonderful :)
    public String Value1 { get; set; }
    public String Value2 { get; set; }

    public CommandResult ExecuteCommand()
    {
        // Does something awesome...
    }
}

Чтобы сделать вещи немного сложнее, мои командные объекты имеют зависимости (сервисы, репозитории и т. Д.), Которые требуются в их соответствующих конструкторах; поэтому мне пришлось создать пользовательский механизм связывания модели, который использовал DependencyResolver по умолчанию (который уже был настроен с моим контейнером IoC) для создания объектов модели:

public class DependencyModelBinder : DefaultModelBinder
{
    protected override Object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        return DependencyResolver.Current.GetService(modelType);
    }
}

И настроить в Global.asax.cs примерно так:

ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();

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

Выпуск: Проблема, с которой я столкнулся, заключается в том, что все мои объекты команд имеют параметр GUID 'SessionId' (который создается из файла cookie), и первое, что они делают, это пытаются разрешить объект сеанса из этого идентификатора, используя внедренный сервис.

public class MyCommand : IMyCommand
{
    public MyCommand (ISessionRepository sessionRepository) { ... }

    public Guid SessionId { get; set; } // Set by model binder from a cookie...

    public CommandResult Execute()
    {
        Session session = SessionRepository.Get(SessionId);

        if (session == null)
            // Do something not so awesome...
    }
}

Я хотел удалить это повторение, поэтому я создал вторую привязку модели, которая позаботилась бы об этом поиске в хранилище, то есть мои объекты команд могли бы иметь свойство Session напрямую (удаляя зависимость конструктора для хранилища сеансов) .

public class SessionModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();

        return sessionRepository.Get((Guid)controllerContext.HttpContext.Request["SessionId"]);
    }
}

Мой Global.asax.cs файл теперь выглядит так:

ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
ModelBinders.Binders.Add(typeof(Session), new SessionModelBinder());

Протестировав SessionModelBinder изолированно, я знаю, что он работает. Однако при использовании его вместе с DependencyModelBinder он никогда не вызывается. Как я могу заставить MVC использовать мой DependencyModelBinder при построении объектов модели, но можно ли использовать его SessionModelBinder при привязке к ним свойств сеанса? Или кто-нибудь знает лучший подход к этому?

1 Ответ

1 голос
/ 19 февраля 2012

Вы можете использовать метод GetPropertyValue в исходной привязке модели, чтобы указать значение для свойства Session:

public class DependencyModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        return DependencyResolver.Current.GetService(modelType);
    }

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        if (propertyDescriptor.Name == "Session")
        {
            var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();
            return sessionRepository.Get(controllerContext.HttpContext.Request["SessionId"]);
        }
        return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }
}
...