ASP.NET MVC에서 컨트롤러 메서드를 오버로드 할 수 있습니까?


327

ASP.NET MVC에서 컨트롤러 메서드를 오버로드 할 수 있는지 궁금합니다. 시도 할 때마다 아래 오류가 발생합니다. 두 가지 방법은 서로 다른 주장을 받아들입니다. 할 수없는 일입니까?

컨트롤러 유형 'MyController'에서 'MyMethod'조치에 대한 현재 요청은 다음 조치 메소드간에 모호합니다.


10
@andy는 mvc 4에서도 동일합니다 :)
basarat

10
mvc 5와 동일
DhruvJoshi

10
mvc 6와 동일
Imad

7
MVC Core 1.1과 동일
kall2sollies

7
MVC Core 2.0과 동일
Guilherme

답변:


201

코드가 오버로드되도록하려면 속성을 사용할 수 있습니다.

[ActionName("MyOverloadedName")]

그러나 동일한 http 메소드에 다른 액션 이름을 사용해야합니다 (다른 사람들이 말했듯이). 그래서 그것은 단지 의미 론적입니다. 오히려 코드 또는 속성에 이름이 있습니까?

Phil은 이와 관련된 기사를 가지고 있습니다 : http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx


5
이것을 사용하고 액션을 오버로드하는 주요 단점은 더 이상 동일한 뷰 파일로 렌더링 할 수 없다는 것입니다.
Jeff Martin

66
실제로는 여전히 동일한 뷰 파일을 렌더링 할 수 있습니다. 맹목적으로 호출하는 대신 뷰 이름을 지정하면됩니다 return View();. 예를 들면 다음과 같습니다 return View("MyOverloadedName");..
EAMann

1
컨트롤러 액션으로 사용 @JD하지만 마이크로 소프트가 말한다 ... 이번 방법 당신은 여기에서 볼 수 있습니다 .. 오버로드 할 수 없습니다 .. asp.net/mvc/tutorials/controllers-and-routing/...
himanshupareek66

@ EAMann 니스, 난 항상 지금까지보기 전체 경로를 정의했다
Alexander Derck

69

예. 각 컨트롤러 메소드 의 HttpGet/ HttpPost(또는 동등한 AcceptVerbs속성)을 고유 한 것으로 즉, HttpGet또는 HttpPost둘 다로 설정 하여이 작업을 수행 할 수있었습니다 . 그렇게하면 요청 유형에 따라 사용할 방법을 알 수 있습니다.

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

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

내가 가진 제안 중 하나는 이와 같은 경우 두 개의 공용 Action 메소드가 코드 중복을 피하기 위해 의존하는 개인 구현을 갖는 것입니다.


1
MVC2로까지 하나 또한 HttpPost / HttpGet 속성을 사용할 수 있습니다
요엘 halb

@yohal 그렇습니다. 여러 동사를 지원할 필요가 없다면 이제 그것을 처리 할 수있는 정식 방법입니다.
tvanfosson

3
REST의 원칙을 위반하기 위해이를 남용하지 않도록주의하십시오.
Fred

1
Show()메소드의 서명이 다르기 때문에 이것이 제대로 작동하는지 확인하십시오 . Get 버전으로 정보를 보내야 할 경우 Get 및 Post 버전은 동일한 서명으로 끝나 ActionName므로이 게시물에 언급 된 속성이나 다른 수정 사항 중 하나 가 필요합니다 .
Scott Fraley

1
@ ScottK.Fraley는 사실입니다. 동일한 서명이 필요한 경우 다른 이름을 지정하고을 적용해야합니다 ActionNameAttribute. 실제로, 나는 그 경우가 거의 없다는 것을 알았습니다.
tvanfosson 2016

42

여기 당신이 할 수있는 또 다른 것이 있습니다 ... 당신은 매개 변수를 가질 수 있고 가질 수없는 방법을 원합니다.

이것을 시도해보십시오 ...

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

이것은 나를 위해 일했습니다 ...이 방법에서는 실제로 들어오는 매개 변수가 있는지 테스트 할 수 있습니다.


문자열에서 잘못된 nullable 구문을 제거하고 기본 매개 변수 값을 사용하도록 업데이트되었습니다.


6
( stringNull을 허용 할 수 없습니다.)
Josh M.

23
문자열은 널 입력 가능할 수 있습니다. 사실, 그것은 이미 nullable이며 '?'가 필요하지 않습니다.
ProfK

9
@ProfK-아니요, 문자열은 null 일 수있는 참조 유형입니다. "널링 가능"하지 않습니다. Nullable은 Nullable <T> (예 : T?)를 사용하고 있음을 의미합니다. 조쉬의 요점은 당신이 넣을 수 없다는 것입니다. 문자열 뒤에는 값 형식이 아니므로 Nullable <T>는 값 형식 만 허용합니다.
Erik Funkenbusch

4
나는이 질문으로 돌아 오는 길을 무작위로 찾은 다음 위의 의견을 게시했습니다. 이것에 대한 기억이 없습니다 ... 이상한! 가 string될 수 없다는 것은 여전히 ​​사실입니다 nullable. 그러나 그것은 될 수 있습니다 null! 어느 쪽이든 내가 진심으로 초기 의견을 게시했습니다.
Josh M.

20

아니요, 아니요 및 아니요. "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");
        }
    }

따라서 "Customer / LoadCustomer"URL을 호출하면 "LoadCustomer"조치가 호출되고 URL 구조 "Customer / LoadCustomerByName"을 사용하여 "LoadCustomer (string str)"가 호출됩니다.

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

이 코드 프로젝트 기사에서 가져온 위의 답변-> MVC 작업 오버로드


고마워 속성을 사용하는 대신 처음부터 다른 작업 이름을 사용할 수도 있습니다.
Dan

1
@Dan 그러나 C #쪽에는 다형성이 없습니다.
Shivprasad Koirala

맞습니다. 컨트롤러 메소드 오버로드는 없지만 HTTP와는 관련이 없습니다.
Chalky

설명해 주셔서 감사합니다. +1. C #이 아닌 더 많은 HTTP를 생각해야합니다. OO 전략으로 행동에 접근 할 이유가 없습니다.

15

이 문제를 극복하기 위해 할 수 쓰기 ActionMethodSelectorAttribute을 검사하는 MethodInfo각 작업과 게시 된 양식 값과 비교를 한 후 (물론, 버튼 이름 제외) 형태의 값이 일치하지 않은 어떤 방법을 거부합니다.

예를 들면 다음과 같습니다 .- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

그러나 이것은 좋은 생각이 아닙니다.


@Cerbrus는 끔찍한 해킹이므로 컨트롤러 코드를 보는 다음 사람은 매우 비표준 접근 방식으로 혼란 스러울 것입니다.
이안 머서

허, 충분합니다.
Cerbrus

14

내가 아는 한 다른 http 메소드를 사용할 때 동일한 메소드 만 가질 수 있습니다.

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

}

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

}

2
장식은 과부하와 관련이 없습니다. 오버로드를 허용하는 매개 변수 목록입니다.
Sky Sanders

@SkySanders 동의하지 않습니다. 매개 변수 기반 오버로드가 MVC 컨트롤러 방법에서 작동하지 않습니다. 실제 예제가 있습니까? 건배.
Chalky

[HttpPost]대신 속성을 사용하십시오 [AcceptVerbs("POST")].
Fred

9

MVC5 의 속성 라우팅 을 사용하여 이것을 달성했습니다 . 분명히 WebForms를 사용하여 수십 년 동안 웹을 개발 한 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)
{
    //.....
}

이것이 도움이되고 누군가를 잘못된 길로 인도하지 않기를 바랍니다. :-)


잘 하셨어요! 방금이 문제에 부딪 쳤습니다. 또한 WebForms에서 "x"년을 보냈으므로 여전히 학습 곡선입니다. 요즘 MVC 없이는 일자리를 얻을 수 없습니다 haha
Tez Wingfield

4

당신은 하나를 사용할 수 있습니다 ActionResult모두 처리 PostGet:

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

메소드 GetPost일치하는 서명이있는 경우 유용합니다 .


1
흠, 바퀴를 다시 재발견하지만 이번에는 사각형 모양입니다. 왜 단순히 [HttpPost / Get] 속성을 사용하지 않습니까?
SOReader

그것은 오래되었지만 MVC가 시그를 일치시키는 두 가지 방법을 구별하지 않았기 때문에 이것을했다고 생각합니다. 다른 방법으로 HttpGet을 넣지 않았지만 HttpPost 속성을 사용하고있었습니다.
DevDave

@DevDave와 두 가지 방법을 모두 사용하려면 system.web.http의 속성이 아닌 system.web.mvc의 속성을 사용해야합니다!
Chalky

4

방금이 질문을 보았는데 지금은 나이가 들었지만 여전히 관련이 있습니다. 아이러니하게도,이 글에서 올바른 의견 하나는 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각각 제 1 및 제 2 인덱스 작업에 매핑합니다.

이 예제 컨트롤러를 살펴보면 위에서 언급 한 기본 경로 패턴을 넘어서는 것이 분명합니다. URL 구조가 코드 명명 규칙과 정확히 일치하면 기본값이 잘 작동하지만 항상 그런 것은 아닙니다. 코드는 도메인을 설명해야하지만 URL은 내용이 SEO 요구 사항과 같은 다른 기준을 기반으로해야하기 때문에 종종 더 나아가 야합니다.

기본 라우팅 패턴의 장점은 고유 한 경로를 자동으로 생성한다는 것입니다. URL은 고유 한 컨트롤러 유형 및 멤버와 일치하므로 컴파일러에서 적용합니다. 자신 만의 경로 패턴을 굴리면 고유성을 보장하고 작동하도록 신중하게 고려해야합니다.

중요 참고 사항 한 가지 단점은 UrlHelper.Action을 사용할 때와 같이 작업 이름을 기반으로하는 경우 오버로드 된 작업에 대한 URL을 생성하기 위해 라우팅을 사용하면 작동하지 않는다는 것입니다. 그러나 이름이 지정된 경로 (예 : UrlHelper.RouteUrl)를 사용하는 경우 작동합니다. 그리고 잘 알려진 소스에 따르면, 명명 된 경로를 사용하는 것은 어떻게 든 갈 수있는 방법입니다 ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

행운을 빕니다!


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");
    }
}

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
    }
}

특히 많은 주장이있는 경우 완벽한 솔루션이 아니지만 그것은 나에게 잘 작동합니다.


1

내 응용 프로그램에서도 동일한 문제에 직면했습니다. Modifiyig에 메소드 정보가 없으면 조치 헤드에 [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);

        }

0

기본 방법을 가상으로 작성

public virtual ActionResult Index()

재정의 된 메서드를 재정의로 만듭니다.

public override ActionResult Index()

편집 : 이것은 override 메소드가 OP의 의도가 아닌 파생 클래스에있는 경우에만 적용됩니다.


2
당신은 아마 그 질문을 오해하고있을 것입니다. OP는 파생 클래스에서 메서드를 재정의하지 않고 동일한 컨트롤러에서 메서드를 오버로드하는 방법을 묻고 있습니다.
에이스

@Andiih : 두 방법이 동일한 컨트롤러에 있으면 어떻게됩니까?
Dharmik Bhandari


0

각 컨트롤러 방법에는 하나의 공개 서명 만 허용됩니다. 과부하를 시도하면 컴파일되지만 런타임 오류가 발생합니다.

오버로드 된 메소드 (작동 할)를 구별하거나 라우팅을 변경 하기 위해 다른 동사 ( [HttpGet][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);
    }

물론 이것은 해킹이므로 나중에 리팩터링해야합니다. 그러나 당분간은 나를 위해 일했습니다.

다음과 같은 디스패처를 작성할 수도 있습니다.

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에는 하나만 필요하다는 것을 알 수 있습니다.


0

지연 돼서 죄송합니다. 나는 같은 문제가 있었고 좋은 답변이있는 링크를 찾았습니다. 새로운 사람들을 도울 수 있습니다.

BinaryIntellect 웹 사이트 및 저자에 대한 모든 크레딧

기본적으로 네 가지 상황이 있습니다 : 다른 동사 사용 , 라우팅 사용 , [NoAction] 속성으로 과부하 표시[ActionName]으로 활동 속성 이름 변경

그래서 그것은 당신의 요구와 상황에 달려 있습니다.

그러나 링크를 따르십시오.

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


-1

다른 모델의 여러 조치에 POST하는 여러보기에 대해 하나의 GET 조치를 사용하려는 경우, 각 POST 조치마다 GET 조치를 추가하여 첫 번째 GET으로 경로 재지 정하여 새로 고침시 404를 방지하십시오.

장거리지만 일반적인 시나리오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.