模拟自定义 ASP.NET 核心方法中"nameof"的行为



nameof构造是 C# 6.0 的一个非常好的功能,尤其是在 ASP.NET Core 中:它避免使用硬编码字符串,如操作名称。相反,我们引用一个类/方法,如果它们的命名发生变化,就会出现编译错误。

例:

public async IActionResult Home() {
return RedirectToAction(nameof(PageController.Index), GetControllerName(nameof(PageController)), new { Area = KnownAreas.Content });
}

与没有nameof的旧版本相比

public async IActionResult Home() {
return RedirectToAction("Index", "Page", new { Area = KnownAreas.Content });
}

由于这很棒,它炸毁了代码:例如,我必须使用GetControllerName定义一个基本控制器类。此方法删除Controller前缀,因为控制器被命名为Page,而不是PageController(最后一种情况将导致 404,因为我们Controller后缀加倍。

由于这是一个常见的用例,并且我希望保持代码尽可能干净,因此我想将该调用减少为如下所示的内容:

public async IActionResult Home() {
return RedirectToActionClean(PageController.Index, PageController, new { Area = KnownAreas.Content });
}

甚至以下(这似乎是不可能的)

public async IActionResult Home() {
return RedirectToActionClean(PageController.Index, new { Area = KnownAreas.Content });
}

在内部,我想使用nameof.所以我PageController.Index传递给我的方法,我在内部将此值作为字符串。但这似乎很困难,因为nameof似乎是一种语言结构,不能设置为泛型等类型。

为了清楚起见,让我们看一下我的RedirectToActionClean标题:

void RedirectToActionClean(??????) {
}

问题是:什么类型可以用于qestion标记,我可以像nameof那样在没有实例的情况下传递任何类型?

我认为这是不可能的,因为它似乎nameof是一种语言结构而不是一种类型。但也许我理解错了什么,有一种方法可以做到这一点。我在最新的Visual Studio 2017上使用 ASP.NET Core 1.1和C# 7。

我过去使用过表达式,效果很好:

//within a controller or base controller
private void SetRouteValues(string action, string controller, RouteValueDictionary routeValues)
{
if (routeValues != null)
{
foreach (var key in routeValues.Keys)
{
RouteData.Values[key] = routeValues[key];
}
}
RouteData.Values["action"] = action;
RouteData.Values["controller"] = controller;
}
protected RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, object>> actionExpression) where TController : Controller
{
var controllerName = typeof(TController).GetControllerName();
var actionName = actionExpression.GetActionName();
var routeValues = actionExpression.GetRouteValues();
SetRouteValues(actionName, controllerName, routeValues);
return new RedirectToRouteResult("Default", RouteData.Values);
}
//a few helper methods
public static class AdditionUrlHelperExtensions
{
public static string GetControllerName(this Type controllerType)
{
var controllerName = controllerType.Name.Replace("Controller", string.Empty);
return controllerName;
}
public static string GetActionName<TController>(this Expression<Func<TController, object>> actionExpression)
{
var actionName = ((MethodCallExpression)actionExpression.Body).Method.Name;
return actionName;
}
public static RouteValueDictionary GetRouteValues<TController>(this Expression<Func<TController, object>> actionExpression)
{
var result = new RouteValueDictionary();
var expressionBody = (MethodCallExpression)actionExpression.Body;
var parameters = expressionBody.Method.GetParameters();
//expression tree cannot represent a call with optional params
//so our method param count and should match the expression body arg count
//but just the same, let's check...
if (parameters.Length != expressionBody.Arguments.Count)
throw new InvalidOperationException("Mismatched parameter/argument count");
for (var i = 0; i < expressionBody.Arguments.Count; ++i)
{
var parameter = parameters[i];
var argument = expressionBody.Arguments[i];
var parameterName = parameter.Name;
var argumentValue = argument.GetValue();
result.Add(parameterName, argumentValue);
}
return result;
}
private static object GetValue(this Expression exp)
{
var objectMember = Expression.Convert(exp, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
}
}

用法:RedirectToAction<HomeController>(c => c.Index("param1", 2, false))

您可以获得很好的编译时安全网,以确保重定向到有效的控制器操作以及正确的参数类型。

最新更新