1

我正在努力改变

@{Html.RenderAction("Foo", "TheAction");}

@{Html.RenderAction((FooController c) => c.TheAction );}

这在很多方面都更好,它改进了:

  • F12-导航
  • 类型使用检测(resharper 等)
  • 方法使用检测
  • 严格
  • ...使死链接远离项目的可能性

...所以我写了一个这样的扩展类:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace V3NET.MVC.Extensions
{
    public static class HtmlHelperExtension
    {
        public static void RenderAction<T>
            (this HtmlHelper helper, Expression<Func<T, Func<int, int?, ActionResult>>> action)
        {
            var actionName =
                (
                    (MethodInfo)
                    (
                        (ConstantExpression)
                        (
                            (MethodCallExpression)
                            (
                                ((UnaryExpression)action.Body).Operand
                            )
                        ).Object
                    ).Value
               ).Name;

            var controllerType = action.Parameters[0].Type;
            var controllerName = new Regex("Controller$").Replace(controllerType.Name, "");

            helper.RenderAction(actionName, controllerName);
        }
    }
}

...但是正如您所看到的,我必须针对采取 int, int? 作为论据。

我如何更一般地表达这一点,这样我就不必编写无数的重载?

4

1 回答 1

2

你可以使用Expression<Action<TController>>.

这是您尝试编写的助手的改进版本,它考虑了助手不支持的更多场景,例如异步控制器操作。它还为参数类型添加了一个通用约束,使其成为控制器:

public static class HtmlHelperExtension
{
    public static void RenderAction<TController>(
        this HtmlHelper helper, 
        Expression<Action<TController>> action,
        object routeValues
    ) where TController: Controller
    {
        var body = action.Body as MethodCallExpression;
        if (body == null)
        {
            throw new ArgumentException("Must be a method call", "action");
        }

        var controllerName = typeof(TController).Name;
        if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
        {
            throw new ArgumentException("Target must end in Controller", "action");
        }
        controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);
        if (controllerName.Length == 0)
        {
            throw new ArgumentException("Cannot route to controller", "action");
        }

        var actionName = GetTargetActionName(body.Method);
        helper.RenderAction(actionName, controllerName, routeValues);
    }

    private static string GetTargetActionName(MethodInfo methodInfo)
    {
        string name = methodInfo.Name;
        if (methodInfo.IsDefined(typeof(NonActionAttribute), true))
        {
            throw new InvalidOperationException(
                string.Format(
                    CultureInfo.CurrentCulture, 
                    "Cannot call non action {0}", 
                    name
                )
            );
        }
        var attribute = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true).OfType<ActionNameAttribute>().FirstOrDefault<ActionNameAttribute>();
        if (attribute != null)
        {
            return attribute.Name;
        }
        if (methodInfo.DeclaringType.IsSubclassOf(typeof(AsyncController)))
        {
            if (name.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
            {
                return name.Substring(0, name.Length - "Async".Length);
            }
            if (name.EndsWith("Completed", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException(
                    string.Format(
                        CultureInfo.CurrentCulture, 
                        "Cannot call {0}Completed method", 
                        name
                    )
                );
            }
        }
        return name;
    }
}
于 2012-10-09T09:44:53.780 回答