Asp.NET MVC ModelBinder, получение метода действия

#asp.net-mvc #asp.net-mvc-2 #asp.net-mvc-3

#asp.net-mvc #asp.net-mvc-2 #asp.net-mvc-3

Вопрос:

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

мой метод действий:

 [MyAttribute]
public ActionResult Index([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
}
  

и здесь типичный ModelBinder

 public class MyModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    {
        // here i would like to get the action method and his "MyAttribute"
    }
}
  

есть предложения, другие решения?

заранее большое спасибо

Комментарии:

1. Это MyAttribute фильтр действий или обычный атрибут CLR? Какова его цель и почему вам нужно связать его с model binder?

2. Да, MyAttribute является атрибутом CLR. Я бы подписал метод «informations», потому что нет способа передать аргументы с помощью [ModelBinder(typeof(MyModelBinder))].

3. @dknaack, есть способ: вы могли бы использовать пользовательский поставщик связующих моделей.

4. Не могли бы вы предоставить пример? Я не думаю, что это решит мою проблему.

5. @dknaack, мне нужно сначала понять вашу проблему и чего вы пытаетесь достичь. Когда эти значения необходимо передать в model binder? Являются ли они специфичными для текущего действия? Я не уверен, что понимаю преимущества использования этого пользовательского атрибута. Какую ценность это приносит? Зачем это нужно связующему модели? Не может ли model binder извлекать эти значения откуда-либо еще. Конкретного примера того, что вы пытаетесь сделать, мне было бы достаточно, чтобы понять ваш сценарий и чего вы пытаетесь достичь.

Ответ №1:

Нет, вы не можете со 100% уверенностью получить текущее действие из model binder. Связыватель модели связан не с действием, а с привязкой к модели. Например, вы можете вызвать

 TryUpdateMode(model)
  

В фильтре перед тем, как было выбрано действие. Также обратите внимание, что метод action может даже не быть методом CLR (см. http://haacked.com/archive/2009/02/17/aspnetmvc-ironruby-with-filters.aspx), на котором можно отразить.

Я думаю, что реальный вопрос заключается в том, чего именно вы пытаетесь достичь и правильно ли это? Если вы хотите, чтобы информация из действия передавалась в model binder (учитывая совет о том, что ваша model binder должна корректно ухудшаться, если информации там нет), вам следует использовать фильтр действий для помещения информации в HttpContext.Элементы (или что-то в этом роде), а затем ваш связующий файл извлекает его.

Метод OnActionExecuting фильтра действий получает ActionExecutingContext, который имеет ActionDescriptor. Для этого вы можете вызвать GetCustomAttributes.

Комментарии:

1. Большое спасибо, Фил! Я знаю, что он не связан (видел это с помощью System. Диагностика. StackFrame). Сложно описать, что я хочу сделать в комментарии. Теперь я сделаю это с помощью ActionFilter и позже расскажу о своем решении в блоге. Если вам интересно, я отправлю вам ссылку через Twitter, если мой пост в блоге будет готов.

Ответ №2:

Вы могли бы попробовать это:

 var actionName = controllerContext.RouteData.GetRequiredString("action");
var myAttribute = (MyAttribute) Attribute.GetCustomAttribute(controllerContext.Controller.GetMethod(actionName), typeof(MyAttribute));
  

Комментарии:

1. Я пробовал это раньше. Но у меня могло бы быть много методов с таким именем и разными аргументами. Итак, «ControllerContext.Controller. GetType().GetMethod(ActionName);» приведет к созданию системы. Отражение. Исключение AmbiguousMatchException

2. конечно, если подпись вашего действия содержит только MyModel, вы можете использовать ControllerContext.Controller. GetMethod(ActionName, new[] {привязкаконтекста.Модель. GetType()}); Но этого, вероятно, будет недостаточно

3. Да, этого недостаточно 😉 Я мог бы использовать методы с этим и другими различиями в аргументах. в любом случае спасибо

Ответ №3:

Вы могли бы переопределить, ControllerActionInvoker.FindAction() чтобы получить атрибут действия и сохранить его в HttpContext.Current.Items , как указано здесь, или расширить ControllerContext.RequestContext , следующим образом:

 public class MyControllerActionInvoker : ControllerActionInvoker
{
    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var action = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (action != null)
        {
            var requestContext = ExtendedRequestContext.Bind(controllerContext);
            var attr = action.GetCustomAttributes(typeof(MyAttribute), false).FirstOrDefault();

            if (attr != null)
                requestContext.CustomAttribute = (MyAttribute)attr;
        }

        return action;
    }
}

public class ExtendedRequestContext : RequestContext
{
    public MyAttribute CustomAttribute { get; set; }

    public static ExtendedRequestContext Bind(ControllerContext controllerContext)
    {
        var requestContext = new ExtendedRequestContext
        {
            HttpContext = controllerContext.RequestContext.HttpContext,
            RouteData = controllerContext.RequestContext.RouteData
        };

        controllerContext.RequestContext = requestContext;
        return requestContext;
    }
}
  

Средство вызова действия по умолчанию заменено либо в конструкторе вашего контроллера, либо на фабрике пользовательских контроллеров:

 public MyController() : base()
{
    ActionInvoker = new MyControllerActionInvoker();
}
  

Кстати, Controller.TempData уже содержит элемент ReflectedParameterDescriptor типа, к которому вы получаете доступ ActionDescriptor , поэтому приведенный выше код может быть избыточным. Однако будьте осторожны, это зависит от конкретной реализации, поэтому со временем может измениться.

Наконец, получите атрибут из этого хранилища в вашем классе binder:

 var requestContext = (ExtendedRequestContext)controllerContext.RequestContext;
if (requestContext.CustomAttribute != null)
{
    // apply your logic here
}