I looked into the source code of MVC 3 and tested it with MVC 4 and discovered how to do it. I incorrectly noted the question ... this is not for MVC 3, I am using MVC 4. Although, since I could find a solution looking at MVC 3 code, it can also work with MVC 3.
In the end ... I hope it costs 5 hours of research, with lots of trial and error.
Works with
- MVC 3 (I think)
- MVC 4 (verified)
Disadvantages of my solution
Unfortunately, this solution is quite complicated and depends on things that I don't really like:
- static object
ControllerBuilder.Current
(very bad for unit testing) - many classes from MVC (high connection is always bad)
- not universal (it works with MVC 3 objects by default, but may not work with other implementations derived from MVC ... for example, a derivative of MvcHandler, custom IControllerFactory, etc.)
- internal dependency (depends on specific aspects of MVC 3 (MVC 4 may also behave) MVC 5 is different ... for example, I know that the
RouteData
object RouteData
not used to find the type of controller, so I just use RouteData stub objects) - mocks of complex objects for data transfer (I needed to make fun of
HttpContextWrapper
and HttpRequestWrapper
to set the http method
as POST
or GET
... these pretty simple values ββcome from complex objects (oh god! = \))
The code
public static Attribute[] GetAttributes( this Controller @this, string action = null, string controller = null, string method = "GET") { var actionName = action ?? @this.RouteData.GetRequiredString("action"); var controllerName = controller ?? @this.RouteData.GetRequiredString("controller"); var controllerFactory = ControllerBuilder.Current .GetControllerFactory(); var controllerContext = @this.ControllerContext; var otherController = (ControllerBase)controllerFactory .CreateController( new RequestContext(controllerContext.HttpContext, new RouteData()), controllerName); var controllerDescriptor = new ReflectedControllerDescriptor( otherController.GetType()); var controllerContext2 = new ControllerContext( new MockHttpContextWrapper( controllerContext.HttpContext.ApplicationInstance.Context, method), new RouteData(), otherController); var actionDescriptor = controllerDescriptor .FindAction(controllerContext2, actionName); var attributes = actionDescriptor.GetCustomAttributes(true) .Cast<Attribute>() .ToArray(); return attributes; }
EDIT
Forgot mocking classes
class MockHttpContextWrapper : HttpContextWrapper { public MockHttpContextWrapper(HttpContext httpContext, string method) : base(httpContext) { this.request = new MockHttpRequestWrapper(httpContext.Request, method); } private readonly HttpRequestBase request; public override HttpRequestBase Request { get { return request; } } class MockHttpRequestWrapper : HttpRequestWrapper { public MockHttpRequestWrapper(HttpRequest httpRequest, string httpMethod) : base(httpRequest) { this.httpMethod = httpMethod; } private readonly string httpMethod; public override string HttpMethod { get { return httpMethod; } } } }
Hope all this helps someone ...
Happy coding for everyone!
source share