繁体   English   中英

ASP.NET MVC 3:有没有办法从类型中获取控制器的字符串名称?

[英]ASP.NET MVC 3 : Is there a way to get a controller's string Name from its Type?

使用MVC3中的UrlHelper ,您可以使用字符串为控制器和操作构建URL:

@Url.Action("Index", "Somewhere")

这会将请求路由到SomewhereController.Index()

我想做的是获取控制器和操作的URL,但是传递控制器的类型:

@Url.Action("Index", typeof(SomewhereController))

有没有办法做到这一点?


编辑/澄清:

我意识到控制器的约定是控制器名称路由到名为{Name}Controller的类,所以我可以从Type.Name的末尾删除'Controller'。 我想我假设有一种方法可以通过一些自定义路由覆盖此约定。 虽然我看得越多,但我不确定是否可能......

也许MVC3 只能路由到名为“* Controller”的类? 我正在通过MVC3源寻找“控制器”硬编码的地方,但尚未找到答案......但如果可以将“Somewhere”路由到SomewhereFoo类而不是SomewhereController ,那么只需删除“控制器“来自班级名称将是不正确的。

如果某人可以向我提供证据支持或反对“控制器”被硬编码到某个地方的MVC3,那么我会对“从名称中移除控制器”的方法感到更舒服。

没有现有的扩展,但您可以编写自己的扩展,以MvcFutures的ActionLink为模型。 我建议使用像@Url.Action<SomewhereController>( c => c.Index )一样的泛型方法@Url.Action<SomewhereController>( c => c.Index )

public static UrlHelperExtensions
{
    public static string Action<TController>( this UrlHelper helper,  Expression<Action<T>> action ) where TController : Controller
    {
        var routeValues = GetRouteValuesFromExpression( action ); 
        return helper.Action( routeValues["action"], routeValues );
    }

    // copied from MvcFutures
    // http://aspnet.codeplex.com/SourceControl/changeset/view/72551#266392
    private static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller
    {
        if (action == null) {
            throw new ArgumentNullException("action");
        }

        MethodCallExpression call = action.Body as MethodCallExpression;
        if (call == null) {
            throw new ArgumentException(MvcResources.ExpressionHelper_MustBeMethodCall, "action");
        }

        string controllerName = typeof(TController).Name;
        if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) {
            throw new ArgumentException(MvcResources.ExpressionHelper_TargetMustEndInController, "action");
        }
        controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);
        if (controllerName.Length == 0) {
            throw new ArgumentException(MvcResources.ExpressionHelper_CannotRouteToController, "action");
        }

        // TODO: How do we know that this method is even web callable?
        //      For now, we just let the call itself throw an exception.

        var rvd = new RouteValueDictionary();
        rvd.Add("Controller", controllerName);
        rvd.Add("Action", call.Method.Name);
        AddParameterValuesFromExpressionToDictionary(rvd, call);
        return rvd;
    }
}
@Url.Action("Index", typeof(SomewhereController).Name.Replace("Controller", ""))

有很多方法可以做到这一点。 如果您使用的是类型,我能想到的最简单的方法是实现Name属性,如下所示:

public static string Name { get { return "Somewhere"; } }

然后像这样调用你的Action:

@Url.Action("Index", SomewhereController.Name);

根据接受的答案

用法

var url = Url.RouteUrl<MyController>(c => c.MyAction(dummy,args));

要么:

@using (Html.BeginForm(Url.Route<MyController>(c => c.MyAction(dummy,args))))
{
    // yadda yadda
}

扩展方法

public static class ActionRouteHelper
{
    /// <summary>
    /// Given a controller/action selector, return its URL route
    /// </summary>
    /// <typeparam name="TController"></typeparam>
    /// <param name="url">helper extended</param>
    /// <param name="action">A lambda for choosing an action from the indicated controller.  May need to provide dummy method arguments.</param>
    /// <returns></returns>
    public static string RouteUrl<TController>(this UrlHelper url, Expression<Action<TController>> action) where TController : Controller
    {
        return url.RouteUrl(url.Route(action));
    }
    /// <summary>
    /// Given a controller/action selector, return its URL route
    /// </summary>
    /// <remarks>See inspiration from https://stackoverflow.com/a/8252301/1037948 </remarks>
    /// <typeparam name="TController">the controller class</typeparam>
    /// <param name="url">helper extended</param>
    /// <param name="action">A lambda for choosing an action from the indicated controller.  May need to provide dummy method arguments.</param>
    /// <returns></returns>
    public static RouteValueDictionary Route<TController>(this UrlHelper url, Expression<Action<TController>> action) where TController : Controller
    {
        // TODO: validate controller name for suffix, non-empty result, if that's important to you here rather than the link just not working
        var controllerName = typeof(TController).Name.Replace("Controller", string.Empty);

        // strip method name out of the expression, the lazy way (https://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression/17220748#17220748)
        var actionName = action.ToString(); // results in something like "c => c.MyAction(arg1,arg2)"
        var startOfMethodName = actionName.IndexOf('.')+1;
        var startOfArgList = actionName.IndexOf('(');
        actionName = actionName.Substring(startOfMethodName, startOfArgList - startOfMethodName);

        return new RouteValueDictionary {
            { "Controller", controllerName },
            { "Action", actionName }
        };
    }
}

你可以创建一个Razor助手:

@helper MyAction(string action, Type controller) {
    var controllerName = typeof(controller)
        .Name.Replace("Controller", "");
    @Url.Action(action, controllerName);
}

然后可以用它代替@Url.Action

@MyAction("ActionName", typeof(MyController))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM