我想改变
@{Html.RenderAction("Foo", "TheAction");}
到
@{Html.RenderAction((FooController c) => c.TheAction );}
在很多方面都更好,它有所提升:
...所以我写了一个像这样的扩展类:
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的动作编写它?作为论点。
我如何更普遍地表达这一点,所以我不必编写大量的重载?
答案 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;
}
}