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))
您可以获得很好的编译时安全网,以确保重定向到有效的控制器操作以及正确的参数类型。