你能在ASP.NET MVC中重载控制器方法吗?

时间:2009-01-12 20:20:21

标签: c# asp.net-mvc overloading

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

  

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

18 个答案:

答案 0 :(得分:199)

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

[ActionName("MyOverloadedName")]

但是,你必须为同一个http方法使用不同的动作名称(正如其他人所说)。所以这就是语义。您希望在代码或属性中使用该名称吗?

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

答案 1 :(得分:69)

是。我已经能够通过将每个控制器方法的HttpGet / HttpPost(或等效AcceptVerbs属性)设置为不同的内容来实现此目的,即HttpGet或{{ 1}},但不是两者。这样它就可以根据请求的类型判断使用哪种方法。

HttpPost

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

答案 2 :(得分:41)

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

为什么不试试这个......

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

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

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

答案 3 :(得分:20)

不,不,不。去试试我们拥有&#34; LoadCustomer&#34;的控制器代码。超载。

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

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

如果您尝试调用&#34; LoadCustomer&#34;动作你会得到错误,如下图所示。

enter image description here

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

为了解决这个问题,我们需要使用&#34; ActionName&#34;属性。

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&#34; Customer / LoadCustomer&#34; &#34; LoadCustomer&#34;将调用action并使用URL结构&#34; Customer / LoadCustomerByName&#34; &#34; LoadCustomer(string str)&#34;将被调用。

enter image description here

enter image description here

我从这个代码项目文章中获取的上述答案 - &gt; MVC Action overloading

答案 4 :(得分:15)

要解决此问题,您可以编写ActionMethodSelectorAttribute来检查每个操作的MethodInfo,并将其与发布的Form值进行比较,然后拒绝任何方法表格值不匹配(当然不包括按钮名称)。

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

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

答案 5 :(得分:13)

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

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

}

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

}

答案 6 :(得分:9)

我在MVC5中Attribute Routing的帮助下实现了这一点。不可否认,我是使用WebForms进行十年Web开发的MVC新手,但以下内容对我有用。与接受的答案不同,这允许所有重载的操作由同一个视图文件呈现。

首先在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)
{
    //.....
}

希望这有助于并且不会导致某人走上错误的道路。 : - )

答案 7 :(得分:4)

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

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

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

答案 8 :(得分:4)

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

如果理解路由,除了简单的{controller} / {action} / {id}默认路由模式之外,很明显可以使用任何唯一模式映射控制器操作。这里有人谈到多态性并且说:&#34; HTTP不理解多态性&#34;,但路由与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
    }
}

这些操作会处理/cars/usa/new-york/cars/usa/texas/dallas等网址,这些网址将分别映射到第一个和第二个索引操作。

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

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

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

祝你好运!

答案 9 :(得分:3)

您可以使用[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");
    }
}

答案 10 :(得分: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
    }
}

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

答案 11 :(得分:1)

我的申请也面临同样的问题。如果没有Modifiyig任何方法信息,我在Action头上提供了[ActionName(&#34; SomeMeaningfulName&#34;)]。问题已解决

[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);

        }

答案 12 :(得分:0)

将基本方法创建为虚拟

public virtual ActionResult Index()

将覆盖的方法创建为覆盖

public override ActionResult Index()

编辑:这显然仅适用于覆盖方法在派生类中,这似乎不是OP的意图。

答案 13 :(得分:0)

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

主要用于从其他控制器继承并希望覆盖基本控制器的操作

ASP.NET MVC - Overriding an action with differing parameters

答案 14 :(得分:0)

每种控制器方法只允许一个公共签名。如果你试图超载它,它会编译,但你得到了你经历过的运行时错误。

如果您不愿意使用不同的动词(例如[HttpGet][HttpPost]属性)来区分重载方法(可行),或更改路由,那么剩下的就是您可以使用不同的名称提供另一种方法,也可以在现有方法内部进行分派。以下是我的表现方式:

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

为了解决这个问题,我做了以下几点:

  1. 将2个重载的操作方法从公共更改为私有
  2. 创建了一个新的公共方法,其中包含&#34;只是&#34; 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);
    }
    
  3. 当然,这是一个黑客,应该稍后重构。但就目前而言,它对我有用。

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

    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只需要一个。

答案 15 :(得分:0)

很抱歉延迟。我遇到了同样的问题,我发现了一个链接,链接的答案很好,这对新手有帮助吗?

BinaryIntellect网站和作者的所有学分

基本上,有四种情况:使用差异动词使用路由具有[NoAction]属性的过载标记 change具有[ActionName]

的操作属性名称

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

不过,请点击链接:

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

答案 16 :(得分:0)

  1. 这个答案是为那些在同一问题上苦苦挣扎的人准备的。你可以 实现您自己的自定义过滤器基于 操作方法选择器属性。 Here 我找到了最好的解决方案 用于解决您的问题。在 .net 5 项目上运行良好。

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

答案 17 :(得分:-1)

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

远射但常见情况。