为@ {Html.RenderAction(“Controller”,“Action”)添加严格性;}。由于方法过载,是否需要大量的扩展方法?

时间:2012-10-09 09:27:03

标签: asp.net-mvc asp.net-mvc-3

我想改变

@{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的动作编写它?作为论点。

我如何更普遍地表达这一点,所以我不必编写大量的重载?

1 个答案:

答案 0 :(得分:2)

您可以使用Expression<Action<TController>>

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

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;
    }
}