C# .NET MVC路由-绑定2个路由映射参数

C# .NET MVC路由-绑定2个路由映射参数,c#,asp.net-mvc,routes,C#,Asp.net Mvc,Routes,我需要像下面这样解析一个url /controller/action/subaction/id 目前,我正在使用开关的子动作,看看究竟需要做什么。例如: public ActionResult Members(string subaction, long id=0) { switch (subaction) { case "Details": var member = _payment.Ge

我需要像下面这样解析一个url

/controller/action/subaction/id
目前,我正在使用开关的子动作,看看究竟需要做什么。例如:

    public ActionResult Members(string subaction, long id=0)
    {
        switch (subaction)
        {
            case "Details":
                var member = _payment.GetMember(id);
                return View("Members_details", member);
            default:
                var members = _payment.GetMembers().ToList();
                return View("Members_list", members);
        }
    }
这是可行的,但我更希望每个事件都有单独的操作,直接从路由访问。如果可能的话,我想在routemap中结合动作和子动作来访问正确的动作

  • /控制器/动作/将调用动作()
  • /控制器/动作/子动作将调用动作\子动作()
  • /控制器/动作/子动作/id将调用动作\子动作(id)

是否可以直接从routemap中执行此操作?

您是说您实际上希望在同一控制器中使用多个“详细信息”操作?如果是这种情况,那么我认为这是开始拆分控制器的一个很好的理由

您可能应该从名为Members的控制器开始,该控制器包含操作详细信息和列表(或索引)

您是否有一个很好的逻辑理由将它们都放在同一个控制器中

或者,您可以调用操作MemberDetails和MemberList以及URL,如

/Controller/MemberDetails/ID
/Controller/MemberList/
给它一个密码:

[ActionName("Member/Details")]
public ActionResult Members_Details(int id){
    return View();
}
自定义操作方法选择器类 如果我是你,我会编写一个操作方法选择器,并使用它来避免在操作中出现分支。我已经编写了一个将采用可选参数的操作分离开来的操作(从而避免了操作简化单元测试中的分支代码)。默认Asp.net MVC定义的路由具有可选的
id
参数

{controller}/{action}/{id}
id = UrlParameter.Optional
因此,有两种行动方法是有意义的:

public ActionResult Index() { ... }
public ActionResult Index(int id) { ... }
我通过编写一个自定义操作选择器过滤器就完成了这一点。下面是一个示例,它描述了整个过程,并提供了一些您可以查看的代码

如何解决你的问题 在您的情况下,这意味着您必须编写一个名为
SubactionAttribute
的自定义操作方法选择器类,然后简单地用它装饰您的操作:

[Subaction("Details")]
public ActionResult Members(long id)
{
    var member = _payment.GetMember(id);
    return View("Members.Details", member);
}

[Subaction] // no name would mean default subaction (when not provided)
public ActionResult Members()
{
    var members = _payment.GetMembers().ToList();
    return View("Members.List", members);
}
我不打算为你写整个课程,但我只会为你指出正确的方向,这样你就可以沿着同样的路径到达你要去的地方:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class SubactionAttribute : ActionMethodSelectorAttribute
{
    #region Properties

    /// <summary>
    /// Gets subaction name.
    /// </summary>
    public string Name { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    public SubactionAttribute()
        : this(null)
    {
        // does nothing
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    /// <param name="subactionName">Sub-action name</param>
    public SubactionAttribute(string subactionName)
    {
        this.Name = subactionName;
    }

    #endregion

    #region ActionMethodSelectorAttribute implementation

    /// <summary>
    /// Determines whether the action method selection is valid for the specified controller context.
    /// </summary>
    /// <param name="controllerContext">The controller context.</param>
    /// <param name="methodInfo">Information about the action method.</param>
    /// <returns>
    /// true if the action method selection is valid for the specified controller context; otherwise, false.
    /// </returns>
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        // get the value of subaction here
        string subName = /* this part you'll have to write */

        // determine whether subaction matches
        return this.Name == subName;
    }

    #endregion
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple=true,Inherited=true)]
公共密封类SubactionAttribute:ActionMethodSelectorAttribute
{
#区域属性
/// 
///获取子操作名称。
/// 
公共字符串名称{get;private set;}
#端区
#区域构造函数
/// 
///初始化类的新实例。
/// 
公共子动作属性()
:此(空)
{
//无所事事
}
/// 
///初始化类的新实例。
/// 
///子动作名称
公共子动作属性(字符串子动作名称)
{
this.Name=子动作名;
}
#端区
#区域操作方法SelectorAttribute实现
/// 
///确定操作方法选择对于指定的控制器上下文是否有效。
/// 
///控制器上下文。
///有关操作方法的信息。
/// 
///如果操作方法选择对指定的控制器上下文有效,则为true;否则为false。
/// 
公共覆盖布尔值是有效的请求(ControllerContext ControllerContext,MethodInfo MethodInfo)
{
如果(controllerContext==null)
{
抛出新ArgumentNullException(“controllerContext”);
}
//在这里获取subaction的值
string subName=/*您必须编写此部分*/
//确定子动作是否匹配
返回此名称。名称==子名称;
}
#端区
}

控制器是项目中的一种模块。一个项目可以有大约30个模块。这意味着我现在已经有30个控制器了。为每个动作创建一个控制器(比如每个模块/控制器5个)将产生150个控制器。另外,不同的控制器可以有相同的操作,这将导致不必要的控制器和URL命名,所以请使用我的替代示例。基本上,使用MVC,您有一个控制器级别和一个操作级别-没有其他级别,例如子操作,我不知道下面的操作是否有效,但尝试将此属性添加到您的操作中,看看会发生什么(这是一个很长的过程)。。。[见编辑后的答案!]@musefan:这肯定不行,因为路线是用斜线切割的。谢谢。我没有想过这样做,但这正是我所需要的。我认为它是完美的awnser,但在实现时,我发现了一个你在博客评论中也讨论过的缺陷:它不考虑重复。这意味着同时使用ID参数的子操作(“详细信息”)和子操作(“编辑”)将失败。@Hugo:实际上这不是问题。以不同的方式命名这些操作,然后对它们使用
[ActionName(“SameName”)]
属性,以便它们在请求级别共享相同的名称。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class SubactionAttribute : ActionMethodSelectorAttribute
{
    #region Properties

    /// <summary>
    /// Gets subaction name.
    /// </summary>
    public string Name { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    public SubactionAttribute()
        : this(null)
    {
        // does nothing
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    /// <param name="subactionName">Sub-action name</param>
    public SubactionAttribute(string subactionName)
    {
        this.Name = subactionName;
    }

    #endregion

    #region ActionMethodSelectorAttribute implementation

    /// <summary>
    /// Determines whether the action method selection is valid for the specified controller context.
    /// </summary>
    /// <param name="controllerContext">The controller context.</param>
    /// <param name="methodInfo">Information about the action method.</param>
    /// <returns>
    /// true if the action method selection is valid for the specified controller context; otherwise, false.
    /// </returns>
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        // get the value of subaction here
        string subName = /* this part you'll have to write */

        // determine whether subaction matches
        return this.Name == subName;
    }

    #endregion
}