代码之家  ›  专栏  ›  技术社区  ›  MedicineMan

无法在控制器“controller”上调用操作方法“system.web.mvc.partialView result foo[t](t)”,因为该操作方法是通用方法

  •  6
  • MedicineMan  · 技术社区  · 14 年前

    无法在控制器“controller”上调用操作方法“system.web.mvc.partialview result foot”,因为该操作方法是泛型方法

    <% Html.RenderAction("Foo", model = Model}); %>
    

    是否有针对ASP MVC 2的此限制的解决方案?我更喜欢使用通用的。我想到的解决方法是将模型类型更改为对象。它有效,但不是首选:

        public PartialViewResult Foo<T>(T model) where T : class
        {
          // do stuff
        }
    
    2 回复  |  直到 6 年前
        1
  •  7
  •   John Farrell    14 年前

    在默认操作描述符内引发的代码:

        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, 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, methodInfo.ReflectedType.FullName, parameterInfo);
                }
            }
    
            // we can call this method
            return null;
        }
    

    因为代码正在调用“methodinfo.containsGenericParameters”,我认为没有一种方法可以在不创建自己的actionDescriptor的情况下重写此行为。从源代码的角度来看,这似乎是不平凡的。

    另一个选项是使控制器类成为通用类,并创建自定义通用控制器工厂。我有一些创建通用控制器的实验代码。虽然很简单,但这只是一个个人实验。

    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 + ",Brainnom.Web", false, true);
    
            Type controllerType = typeof(GenericController<>).MakeGenericType(actionModelType);
    
            var controllerBase = Activator.CreateInstance(controllerType, new object[0] {}) as IController;
    
            return controllerBase;
        }
    }
    
        2
  •  1
  •   Soma Mbadiwe    6 年前

    六年后,当MVC5(和MVC6)在镇上时,我遇到了同样的问题。我正在用MVC5构建我的站点,这样我就可以安全地假设它还没有得到现成的支持。我登陆这里寻求解决办法。好吧,我最终找到了一种不侵入控制器或其工厂的方法来修复它,特别是因为我只在少数地方需要这个特性。

    方法:稍微修改 Command Pattern (反正我已经在代码中使用过了)。

    对于这个问题,首先定义接口

    public interface IMyActionProcessor
    {
        PartialViewResult Process<T>(T theModel);
    }
    

    以及相应的实施:

    public sealed class MyActionProcessor : IMyActionProcessor
    {
        // Your IoC container. I had it explicitly mapped just like any other 
        // registration to cause constructor injection. Using SimpleInjector, it will be 
        // <code>Container.Register(typeof(IServiceProvider), () => Container);</code>
        private IServiceProvider _serviceProvider;
        public MyActionProcessor(IServiceProvider serviceProvider) 
        {
            _serviceProvider = serviceProvider;
        }
        public PartialViewResult Process<T>(T theModel)
        {
            var handlerType =
                typeof(IMyActionHandler<>).MakeGenericType(theModel.GetType());
    
            dynamic handler = _serviceProvider.GetService(handlerType);
    
            return handler.Handle((dynamic)theModel);
        }
    }
    

    处理程序(上面代码中的userd)如下所示:

    public interface IMyActionHandler<T> where T : class
    {
        PartialViewResult Execute(T theModel);
    }
    

    有了以上这些,您现在所要做的就是根据类的具体情况为处理程序提供实现。 T . 像这样:

    public class ModelClassHandler : IMyActionHandler<ModelClass>
    {
        public PartialViewResult Execute(ModelClass theModel);
        {
            // Do Stuff here and return a PartialViewResult instance
        }
    }
    

    有了以上所有功能,您现在可以在控制器中简单地执行以下操作:

    var processor = YourServiceLocator.GetService<IMyActionProcessor>();
    var model = new ModelClass { /* Supply parameters */ };
    
    var partialViewResult = processor.Process(model);
    

    我知道这是一种额外的间接性,但效果很好。在我的例子中,我继续扩展处理程序和处理器,以便它们可以返回我想要的任何东西,而不仅仅是 PartialViewResult .

    如果有一个更简单的解决方案的话,我会很感兴趣的。我想使用MVC时出现这种问题是不常见的。

    PS:任何好的IOC容器都应该能够通过扫描程序集注册开放的泛型。因此,一旦正确配置,就不需要显式注册处理程序接口的实现。

    PPS: This post provides better insight to the answer given here 特别是关于如何推广这个解。