339

我很想知道您是否可以在 ASP.NET MVC 中重载控制器方法。每当我尝试时,我都会收到以下错误。这两种方法接受不同的参数。这是不能做的事情吗?

当前对控制器类型“MyController”的操作“MyMethod”的请求在以下操作方法之间不明确:

4

18 回答 18

207

如果您希望代码进行重载,可以使用该属性。

[ActionName("MyOverloadedName")]

但是,您必须为同一个 http 方法使用不同的操作名称(正如其他人所说)。所以这只是当时的语义。您宁愿在代码中使用名称还是在属性中使用名称?

Phil 有一篇与此相关的文章:http: //haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

于 2009-01-12T20:37:43.083 回答
70

是的。我已经能够通过将每个控制器方法的HttpGet/ HttpPost(或等效AcceptVerbs属性)设置为不同的东西来做到这一点,即HttpGetHttpPost,但不能同时使用两者。这样它就可以根据请求的类型来判断使用哪种方法。

[HttpGet]
public ActionResult Show()
{
   ...
}

[HttpPost]
public ActionResult Show( string userName )
{
   ...
}

我的一个建议是,对于这样的情况,应该有一个私有实现,你的两个公共 Action 方法都依赖它来避免重复代码。

于 2009-01-12T20:33:55.463 回答
44

这是您可以做的其他事情......您想要一个能够有参数而不是没有参数的方法。

为什么不试试这个...

public ActionResult Show( string username = null )
{
   ...
}

这对我有用......在这种方法中,您实际上可以测试是否有传入参数。


更新以删除字符串上无效的可为空语法并使用默认参数值。

于 2011-03-29T06:22:27.750 回答
23

不,不,不。去试试下面我们有“LoadCustomer”重载的控制器代码。

public class CustomerController : Controller
    {
        //
        // GET: /Customer/

        public ActionResult LoadCustomer()
        {
            return Content("LoadCustomer");
        }
        public ActionResult LoadCustomer(string str)
        {
            return Content("LoadCustomer with a string");
        }
    }

如果您尝试调用“LoadCustomer”操作,您将收到如下图所示的错误。

在此处输入图像描述

多态性是 C# 编程的一部分,而 HTTP 是一种协议。HTTP 不理解多态性。HTTP 适用于概念或 URL,而 URL 只能具有唯一名称。所以HTTP没有实现多态。

为了解决同样的问题,我们需要使用“ActionName”属性。

public class CustomerController : Controller
    {
        //
        // GET: /Customer/

        public ActionResult LoadCustomer()
        {
            return Content("LoadCustomer");
        }

        [ActionName("LoadCustomerbyName")]
        public ActionResult LoadCustomer(string str)
        {
            return Content("LoadCustomer with a string");
        }
    }

因此,现在如果您调用 URL“Customer/LoadCustomer”,将调用“LoadCustomer”操作,并使用 URL 结构“Customer/LoadCustomerByName”调用“LoadCustomer(string str)”。

在此处输入图像描述

在此处输入图像描述

我从这篇 codeproject 文章中得到的上述答案 --> MVC Action 重载

于 2014-11-27T23:48:25.570 回答
15

为了克服这个问题,您可以编写一个ActionMethodSelectorAttribute检查MethodInfo每个操作的方法并将其与发布的表单值进行比较,然后拒绝任何表单值不匹配的方法(当然不包括按钮名称)。

这是一个示例:- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

但是,这不是一个好主意。

于 2010-02-17T01:09:53.867 回答
14

据我所知,您只能在使用不同的 http 方法时使用相同的方法。

IE

[AcceptVerbs("GET")]
public ActionResult MyAction()
{

}

[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{

}
于 2009-01-12T20:33:28.723 回答
10

我在 MVC5中的属性路由的帮助下实现了这一点。诚然,我是使用 WebForms 进行 Web 开发十年的新手,但以下内容对我有用。与公认的答案不同,这允许所有重载的动作由同一个视图文件呈现。

首先在 App_Start/RouteConfig.cs 中启用属性路由。

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapMvcAttributeRoutes();

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );            
    }
}

可以选择使用默认路由前缀装饰您的控制器类。

[RoutePrefix("Returns")]
public class ReturnsController : BaseController
{
    //.......

然后用一个共同的路线和参数来装饰你的控制器动作,这些动作相互过载。使用类型约束参数,您可以使用具有不同类型 ID 的相同 URI 格式。

[HttpGet]
// Returns
public ActionResult Index()
{
    //.....
}

[HttpGet]
[Route("View")]
// Returns/View
public ActionResult View()
{
    // I wouldn't really do this but it proves the concept.
    int id = 7026;
    return View(id);
}

[HttpGet]
[Route("View/{id:int}")]
// Returns/View/7003
public ActionResult View(int id)
{
    //.....
}

[HttpGet]
[Route("View/{id:Guid}")]
// Returns/View/99300046-0ba4-47db-81bf-ba6e3ac3cf01
public ActionResult View(Guid id)
{
    //.....
}

希望这会有所帮助,并且不会导致某人走错路。:-)

于 2015-06-03T09:32:36.560 回答
5

您可以使用单个ActionResult来处理PostGet

public ActionResult Example() {
   if (Request.HttpMethod.ToUpperInvariant() == "GET") {
    // GET
   }
   else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
     // Post  
   }
}

如果您的GetPost方法具有匹配的签名,则很有用。

于 2012-12-03T14:18:52.963 回答
5

我刚刚遇到这个问题,尽管它现在已经很老了,但它仍然非常相关。具有讽刺意味的是,该线程中的一个正确评论是由一位自认是 MVC 初学者在撰写帖子时发布的。甚至 ASP.NET 文档也不完全正确。我有一个大项目,我成功地重载了动作方法。

如果人们理解路由,除了简单的 {controller}/{action}/{id} 默认路由模式之外,很明显控制器操作可以使用任何独特的模式进行映射。这里有人谈及多态,说:“HTTP 不懂多态”,但路由与 HTTP 无关。简单地说,它是一种字符串模式匹配的机制。

完成这项工作的最佳方法是使用路由属性,例如:

[RoutePrefix("cars/{country:length(3)}")]
public class CarHireController
{
    [Route("{location}/{page:int=1}", Name = "CarHireLocation")]
    public ActionResult Index(string country, string location, int page)
    {
        return Index(country, location, null, page);
    }

    [Route("{location}/{subLocation}/{page:int=1}", Name = "CarHireSubLocation")]
    public ActionResult Index(string country, string location, string subLocation, int page)
    {
        //The main work goes here
    }
}

这些操作将处理 和 之类的 url /cars/usa/new-york/cars/usa/texas/dallas它们将分别映射到第一个和第二个 Index 操作。

检查这个示例控制器很明显,它超出了上面提到的默认路由模式。如果您的 url 结构与您的代码命名约定完全匹配,则默认设置效果很好,但情况并非总是如此。代码应该是对域的描述,但 url 通常需要更进一步,因为它们的内容应该基于其他标准,例如 SEO 要求。

默认路由模式的好处是它会自动创建唯一的路由。这是由编译器强制执行的,因为 url 将匹配唯一的控制器类型和成员。滚动您自己的路线模式需要仔细考虑以确保唯一性并且它们有效。

重要说明一个缺点是使用路由为重载操作生成 url 在基于操作名称时不起作用,例如,在使用 UrlHelper.Action 时。但如果使用命名路由,例如 UrlHelper.RouteUrl,它确实有效。根据备受推崇的消息来源,使用命名路由是无论如何都要走的路(http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/)。

祝你好运!

于 2016-02-04T17:10:11.923 回答
4

您可以使用 [ActionName("NewActionName")] 来使用具有不同名称的相同方法:

public class HomeController : Controller
{
    public ActionResult GetEmpName()
    {
        return Content("This is the test Message");
    }

    [ActionName("GetEmpWithCode")]
    public ActionResult GetEmpName(string EmpCode)
    {
        return Content("This is the test Messagewith Overloaded");
    }
}
于 2017-07-15T09:26:49.643 回答
2

我需要一个重载:

public ActionResult Index(string i);
public ActionResult Index(int groupId, int itemId);

我最终这样做的论据很少:

public ActionResult Index(string i, int? groupId, int? itemId)
{
    if (!string.IsNullOrWhitespace(i))
    {
        // parse i for the id
    }
    else if (groupId.HasValue && itemId.HasValue)
    {
        // use groupId and itemId for the id
    }
}

这不是一个完美的解决方案,特别是如果你有很多争论,但它对我来说效果很好。

于 2013-06-10T21:57:00.233 回答
1

我在我的申请中也遇到了同样的问题。在没有修改任何方法信息的情况下,我在动作头上提供了 [ActionName("SomeMeaningfulName")]。问题解决了

[ActionName("_EmployeeDetailsByModel")]
        public PartialViewResult _EmployeeDetails(Employee model)
        {
            // Some Operation                
                return PartialView(model);
            }
        }

[ActionName("_EmployeeDetailsByModelWithPagination")]
        public PartialViewResult _EmployeeDetails(Employee model,int Page,int PageSize)
        {

                // Some Operation
                return PartialView(model);

        }
于 2016-05-27T11:43:29.350 回答
0

将基本方法创建为虚拟

public virtual ActionResult Index()

创建被覆盖的方法作为覆盖

public override ActionResult Index()

编辑:这显然只适用于重写方法在派生类中,而派生类似乎不是 OP 的意图。

于 2012-02-28T14:40:51.077 回答
0

我喜欢在另一个帖子中发布的这个答案

这主要用于如果您从另一个控制器继承并希望覆盖来自基本控制器的操作

ASP.NET MVC - 用不同的参数覆盖一个动作

于 2013-07-02T11:52:01.610 回答
0

每个控制器方法只允许一个公共签名。如果您尝试重载它,它会编译,但您会遇到您遇到的运行时错误。

如果您不愿意使用不同的动词(如[HttpGet]and[HttpPost]属性)来区分重载方法(这将起作用)或更改路由,那么剩下的就是您可以提供另一个名称不同的方法,或者您可以在现有方法中调度。我是这样做的:

我曾经遇到过必须保持向后兼容性的情况。原始方法需要两个参数,但新方法只有一个。以我预期的方式重载不起作用,因为 MVC 不再找到入口点。

为了解决这个问题,我做了以下事情:

  1. 将 2 个重载的操作方法从公共更改为私有
  2. 创建了一个包含“仅”2 个字符串参数的新公共方法。那个充当调度员,即:

    public ActionResult DoSomething(string param1, string param2)
    {
        if (string.IsNullOrEmpty(param2))
        {
            return DoSomething(ProductName: param1);
        }
        else
        {
            int oldId = int.Parse(param1);
            return DoSomething(OldParam: param1, OldId: oldId);
        }
    }
    
    
    private ActionResult DoSomething(string OldParam, int OldId)
    {
        // some code here
        return Json(result);
    }
    
    
    private ActionResult DoSomething(string ProductName)
    {
        // some code here
        return Json(result);
    }
    

当然,这是一个 hack,应该稍后重构。但就目前而言,它对我有用。

您还可以创建一个调度程序,如:

public ActionResult DoSomething(string action, string param1, string param2)
{
    switch (action)
    {
        case "update":
            return UpdateAction(param1, param2);
        case "remove":
            return DeleteAction(param1);
    }
}

可以看到,UpdateAction 需要 2 个参数,而 DeleteAction 只需要一个。

于 2017-08-28T13:59:20.657 回答
0

抱歉耽搁了。我遇到了同样的问题,我找到了一个很好答案的链接,这对新人有帮助吗

BinaryIntellect 网站和作者的所有学分

基本上有四种情况:使用不同的动词使用路由使用[NoAction]属性重载标记和使用[ActionName]更改动作属性名称

所以,这取决于你的要求和你的情况。

但是,请点击链接:

链接: http: //www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx

于 2019-07-25T12:35:44.800 回答
0
  1. 对于那些在同一问题上苦苦挣扎的人来说,这个答案。您可以基于 ActionMethodSelectorAttribute 实现自己的自定义过滤器。在这里,我找到了解决您问题的最佳解决方案。在 .net 5 项目上运行良好。

  2. 如果您尝试实现与 Web api 控制器中相同的逻辑,请使用 Microsoft.AspNetCore.Mvc.WebApiCompatShim。此 nuget 包提供 ASP.NET Core MVC 与 ASP.NET Web API 2 的兼容性,以简化现有 Web API 实现的迁移。请检查此答案,但请考虑从 ASP.NET Core 3.0 开始,Microsoft.AspNetCore.Mvc.WebApiCompatShim 包不再可用。

于 2021-05-27T12:03:31.093 回答
-1

如果这是尝试对多个视图使用一个 GET 操作,这些视图 POST 到具有不同模型的多个操作,则尝试为每个重定向到第一个 GET 的 POST 操作添加一个 GET 操作,以防止刷新时出现 404。

远射但常见的场景。

于 2016-09-12T16:19:58.380 回答