无法在控制器’Controller’上调用操作方法’System.Web.Mvc.PartialViewResult FooT’,因为action方法是泛型方法
<% Html.RenderAction("Foo",model = Model}); %>
ASP MVC 2有这种限制的解决方法吗?我真的更喜欢使用泛型.我提出的解决方法是将模型类型更改为对象.它有效,但不是首选:
public PartialViewResult Foo<T>(T model) where T : class { // do stuff }
解决方法
抛出的代码在默认的ActionDescriptor中:
internal static string VerifyActionMethodIsCallable(MethodInfo methodInfo) { // we can't call instance methods where the 'this' parameter is a type other than ControllerBase if (!methodInfo.IsStatic && !typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) { return String.Format(CultureInfo.CurrentUICulture,MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType,methodInfo,methodInfo.ReflectedType.FullName); } // we can't call methods with open generic type parameters if (methodInfo.ContainsGenericParameters) { return String.Format(CultureInfo.CurrentUICulture,MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods,methodInfo.ReflectedType.FullName); } // we can't call methods with ref/out parameters ParameterInfo[] parameterInfos = methodInfo.GetParameters(); foreach (ParameterInfo parameterInfo in parameterInfos) { if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) { return String.Format(CultureInfo.CurrentUICulture,MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters,methodInfo.ReflectedType.FullName,parameterInfo); } } // we can call this method return null; }
因为代码调用“methodInfo.ContainsGenericParameters”,所以我认为没有办法在不创建自己的ActionDescriptor的情况下覆盖此行为.从浏览源代码看,这似乎并非无足轻重.
另一个选择是使您的控制器类通用并创建一个自定义通用控制器工厂.我有一些实验代码可以创建一个通用控制器.它的hacky但它只是一个个人实验.
public class GenericControllerFactory : DefaultControllerFactory { protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext,string controllerName) { //the generic type parameter doesn't matter here if (controllerName.EndsWith("Co"))//assuming we don't have any other generic controllers here return typeof(GenericController<>); return base.GetControllerType(requestContext,controllerName); throw new InvalidOperationException("Generic Factory wasn't able to resolve the controller type"); } protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext,Type controllerType) { //are we asking for the generic controller? if (requestContext.RouteData.Values.ContainsKey("modelType")) { string typeName = requestContext.RouteData.Values["modelType"].ToString(); //magic time return GetGenericControllerInstance(typeName,requestContext); } if (!typeof(IController).IsAssignableFrom(controllerType)) throw new ArgumentException(string.Format("Type requested is not a controller: {0}",controllerType.Name),"controllerType"); return base.GetControllerInstance(requestContext,controllerType); } /// <summary> /// Returns the a generic IController tied to the typeName requested. /// Since we only have a single generic controller the type is hardcoded for now /// </summary> /// <param name="typeName"></param> /// <returns></returns> private IController GetGenericControllerInstance(string typeName,RequestContext requestContext) { var actionName = requestContext.RouteData.Values["action"]; //try and resolve a custom view model Type actionModelType = Type.GetType("Brainnom.Web.Models." + typeName + actionName + "viewmodel,Brainnom.Web",false,true) ?? Type.GetType("Brainnom.Web.Models." + typeName + ",true); Type controllerType = typeof(GenericController<>).MakeGenericType(actionModelType); var controllerBase = Activator.CreateInstance(controllerType,new object[0] {}) as IController; return controllerBase; } }