#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
}