C# 模仿“的行为”;名称;在自定义ASP.NET核心方法中
C# 模仿“的行为”;名称;在自定义ASP.NET核心方法中,c#,asp.net-core,asp.net-core-mvc,c#-6.0,C#,Asp.net Core,Asp.net Core Mvc,C# 6.0,nameof构造是C#6.0的一个非常好的特性,特别是在ASP.NET内核中:它避免了使用硬编码字符串,比如操作名称。相反,我们引用了一个类/方法,如果它们的命名发生了变化,就会得到相应的错误 例如: public async IActionResult Home() { return RedirectToAction(nameof(PageController.Index), GetControllerName(nameof(PageController)), new { Area =
nameof
构造是C#6.0的一个非常好的特性,特别是在ASP.NET内核中:它避免了使用硬编码字符串,比如操作名称。相反,我们引用了一个类/方法,如果它们的命名发生了变化,就会得到相应的错误
例如:
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
定义一个基本控制器类。此方法删除控制器
前缀,因为控制器名为页面
,而不是页面控制器
(最后一种情况将导致404,因为我们将控制器
后缀加倍
由于这是一个常见的用例,我希望尽可能保持代码的整洁,因此我希望将该调用简化为以下内容:
public async IActionResult Home() {
return RedirectToActionClean(PageController.Index, PageController, new { Area = KnownAreas.Content });
}
甚至以下(这似乎不可能)
在内部,我想使用nameof
。因此我将PageController.Index
传递给我的方法,并且在内部将该值作为字符串。但这似乎很困难,因为nameof
似乎是一个语言构造,不能像泛型一样设置为类型
为了清楚起见,让我们看看我的重定向到ActionClean
标题:
void RedirectToActionClean(??????) {
}
问题是:什么类型可以用于qestion标记,我可以在没有实例的情况下传递任何类型,比如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();
}
}
//在控制器或基本控制器内
私有void SetRouteValues(字符串操作、字符串控制器、RouteValueDictionary routeValues)
{
if(routeValue!=null)
{
foreach(routeValues.Keys中的var键)
{
RouteData.Values[键]=RouteValue[键];
}
}
路由数据值[“操作”]=操作;
路由数据值[“控制器”]=控制器;
}
受保护的RedirectToRouteResult RedirectToAction(表达式actionExpression),其中TController:Controller
{
var controllerName=typeof(TController).GetControllerName();
var actionName=actionExpression.GetActionName();
var routeValues=actionExpression.GetRouteValues();
SetRouteValues(actionName、controllerName、routeValues);
返回新的RedirectToRouteResult(“默认”,routedData.Values);
}
//几种辅助方法
公共静态类AdditionUrlHelperExtensions
{
公共静态字符串GetControllerName(此类型controllerType)
{
var controllerName=controllerType.Name.Replace(“Controller”,string.Empty);
返回控制器名称;
}
公共静态字符串GetActionName(此表达式actionExpression)
{
var actionName=((MethodCallExpression)actionExpression.Body).Method.Name;
返回actionName;
}
公共静态RouteValueDictionary GetRouteValues(此表达式actionExpression)
{
var result=新的RouteValueDictionary();
var expressionBody=(MethodCallExpression)actionExpression.Body;
var parameters=expressionBody.Method.GetParameters();
//表达式树不能表示具有可选参数的调用
//因此,我们的方法param count和应该匹配表达式体arg count
//但还是一样,让我们检查一下。。。
if(parameters.Length!=expressionBody.Arguments.Count)
抛出新的InvalidOperationException(“参数/参数计数不匹配”);
对于(var i=0;i
用法:RedirectToAction(c=>c.Index(“param1”,2,false))
您可以获得一个很好的编译时安全网,确保重定向到有效的控制器操作以及正确的参数类型。
typeof(Class).Name
???我已经尝试过这种方法,问题是:我需要将Class
作为参数传递,它似乎只能作为像new PageController()这样的对象.Index
。仅为生成链接而创建控制器实例来浪费资源似乎不是一个好主意。这就是我喜欢nameof
的原因,因为nameof(PageController.Index)
在没有PageController
实例的情况下是可能的。控制器也有同样的问题。唯一的解决办法似乎不是像我的第一个示例中那样使用包装器,但这会破坏代码。我认为C中没有任何其他语言构造允许您直接使用实例的声明一种替代方法是声明使用表达式,比如:RedirectToAction(c=>c.Index())
,但是如果控制器操作需要参数,那么使用它将是一件非常痛苦的事情,RedirectToActionClean(nameof(PageController.Index))…)
?它又长又冗余。鉴于控制器的名称不包含控制器
后缀,我甚至不能简单地使用nameof(PageController)
。因此,我需要一些帮助函数,将PageController
转换为Page
。这个洞调用可以缩短为重定向到ActionClean(x=>x.Index(),KnownArea.Content)
//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();
}
}