0

В MVC3 (.Net) можно установить Bind атрибут типа параметра в сигнатуре метода для метода контроллера:влияющие на ModelBinders MVC с атрибутами

[HttpPost] 
public ActionResult Edit([Bind(Exclude = "Name")]User user) 
{ 
    ... 
} 

я написал несколько пользовательских ModelBinders. Было бы хорошо, чтобы иметь возможность влиять на их поведение в зависимости от атрибутов, установленных на тип параметра, например, так:

[HttpPost] 
public ActionResult Edit([CustomModelBinderSettings(DoCustomThing = "True")]User user) 
{ 
    ... 
} 

Однако, я не могу показаться, чтобы найти способ восстановить данные атрибутов. Это возможно?


Редактировать

Я пытаюсь получить доступ к AttributeData внутри пользовательского ModelBinder. В приведенном ниже примере «настройки» всегда равны нулю

public class TestBinder : DefaultModelBinder { 
     public override object BindModel(
      ControllerContext controllerContext, 
      ModelBindingContext bindingContext) { 

      //Try and get attribute from ModelType 
      var settings = (CustomModelBinderSettingsAttribute) 
       TypeDescriptor.GetAttributes(bindingContext.ModelType)[typeof(CustomModelBinderSettingsAttribute)]; 

      ... 

Спасибо за любую помощь.

+0

Смотрите здесь: [Поиск пользовательских атрибутов свойств вид модели] [1] [1 ]: http://stackoverflow.com/questions/6205176/finding-custom-attributes-on-view-model-properties-when-model-binding – mwjackson

ответ

0

Ответ: не возможно :(

Я ступил через исходный код MVC, и в конце концов нашел, что класс ControllorActionInvoker явно обращается атрибут связывания с параметрами метода действия, и устанавливает их на свойство bindingContext. Без переопределения или перезаписи больших частей инфраструктуры MVC невозможно, чтобы атрибуты, добавленные в параметры действия, были доступны из ModelBinder.

Однако можно получить атрибуты, установленные на ViewModel используя код, показанный на LukLeds пост:

var attr = ("GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)]; 

Увы, это не то, что я решил сделать в этом случае, но это придется делать сейчас.

+0

Неверно, см. Мой ответ ниже – mwjackson

0

Вам не нужно ничего делать, кроме того, что вы сделали, чтобы получить доступ к значению свойства DoCustomThing из вашего CustomModelBinderAttribute. Где вы пытаетесь прочитать значение DoCustomThing, что он недоступен?

public class CustomModelBinderSettingsAttribute : CustomModelBinderAttribute 
{ 
    public string DoCustomThing { get; set; } 
    public override IModelBinder GetBinder() 
    { 
     // Pass the value of DoCustomThing to the custom model binder instance. 
     MyCustomizableModelBinder binder = new MyCustomizableModelBinder(this.DoCustomThing); 
     return binder; 
    } 
} 
+0

Спасибо за ответ. Я обновил свой вопрос, чтобы включить пример контекста, к которому я пытаюсь получить доступ к данным атрибута от – James

0

Это возможно, если у вас есть (или создать новый) AuthorizeAttribute (атрибут фильтра), который будет хранить filterContext.ActionDescriptor (ваше действие) в HttpContext.Current.Items, а затем в ModelBinder, которое будет извлечено. Имея ActionDescriptor, вы можете найти нужный параметр путем привязкиContext.ModelName и проверить наличие атрибута.

Things отметить:

  • ModelBinder не связан действий выполняется
  • с помощью ControllerContext вы можете получить ActionName называется, но не фактический метод называется (может иметь несколько перегруженных с вашим параметром)
  • AuthorizeFilter вызывается перед ModelBinder, нормальный ActionFilter вызывается после ModelBinder
0

Вы можете получить атрибуты действия и его параметров с помощью " перехват»Controller.ActionInvoker.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); 

      // if necessary, use action.GetCustomAttributes() to add action's attributes 

      foreach (var parameter in action.GetParameters()) 
      { 
       var attr = parameter.GetCustomAttributes(typeof(MyBinderAttribute), false).FirstOrDefault(); 
       if (attr != null) 
        requestContext.Attributes.Add(parameter.ParameterName, (MyBinderAttribute)attr); 
      } 
     } 

     return action; 
    } 
} 

public class ExtendedRequestContext : RequestContext 
{ 
    public Dictionary<string, MyBinderAttribute> Attributes { get; private set; } 

    public static ExtendedRequestContext Bind(ControllerContext controllerContext) 
    { 
     var requestContext = new ExtendedRequestContext 
     { 
      HttpContext = controllerContext.RequestContext.HttpContext, 
      RouteData = controllerContext.RequestContext.RouteData, 
      Attributes = new Dictionary<string, MyBinderAttribute>() 
     }; 

     controllerContext.RequestContext = requestContext; 
     return requestContext; 
    } 
} 

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

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

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

И наконец, получить атрибуты из этого хранилища в классе связующего:

var requestContext = (ExtendedRequestContext)controllerContext.RequestContext; 

if (requestContext.Attributes.ContainsKey(bindingContext.ModelName)) 
{ 
    var attr = requestContext.Attributes[bindingContext.ModelName]; 
    // apply your logic here 
}