ASP.NET MVCでコントローラーメソッドをオーバーロードできますか?


327

ASP.NET MVCでコントローラーメソッドをオーバーロードできるかどうか知りたいです。試行するたびに、以下のエラーが発生します。2つのメソッドは異なる引数を受け入れます。これはできませんか?

コントローラタイプ 'MyController'に対するアクション 'MyMethod'の現在のリクエストは、次のアクションメソッド間であいまいです


10
@andy mvc 4でも同じです:)
basarat 2013

10
MVC 5でも同じ
DhruvJoshi 2015

10
MVC 6でも同じ
Imad

7
MVC Core 1.1でも同じ
kall2sollies 2017年

7
MVCコア2.0でも同じ
ギルヘルム2017

回答:


201

コードでオーバーロードを実行する場合は、属性を使用できます。

[ActionName("MyOverloadedName")]

ただし、同じHTTPメソッドには別のアクション名を使用する必要があります(他の人が言ったように)。つまり、その時点では単なる意味論です。コードまたは属性に名前を入れますか?

Philはこれに関連する記事を持っています:http : //haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx


5
これを使用してアクションをオーバーロードする主な欠点は、同じビューファイルでレンダリングできなくなることです。
ジェフマーティン

66
実際には、同じビューファイルをレンダリングできます。盲目的に呼び出すのではなく、ビューの名前を指定するだけですreturn View();。例:return View("MyOverloadedName");
EAMann 2011

1
@JDしかしMicrosoftは...コントローラのアクションとして使用する方法が過負荷にすることができないと言う..あなたはここでそれを見ることができます。.. 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 )
{
   ...
}

私の提案の1つは、このような場合、両方のパブリックアクションメソッドがコードの重複を避けるために依存するプライベート実装を持つことです。


1
MVC2以上では、HttpPost / HttpGet属性を使用することもできます
yoel halb

@yohalはい、複数の動詞をサポートする必要がない場合、それは今それを処理する標準的な方法です。
tvanfosson

3
これを悪用してRESTの原則に違反しないように注意してください。
フレッド

1
Show()メソッドのシグネチャが異なるため、これが機能していることを確認してください。Getバージョンに情報を送信する必要がある場合、GetバージョンとPostバージョンは同じ署名で終了しActionName、この投稿で言及されている属性または他の修正のいずれかが必要になります。
Scott Fraley、

1
@ ScottK.Fraleyそれは本当です。同じ署名が必要な場合は、名前を変えてを適用する必要がありますActionNameAttribute。実際には、それが事実であることはめったにありません。
tvanfosson 2016

42

ここにあなたができる他の何かがあります...あなたはパラメータを持つことができて、持たないことができるメソッドが欲しいです。

これを試してみませんか...

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

これは私にとってはうまくいきました...そしてこの1つの方法では、実際に着信パラメーターがあるかどうかをテストすることができます。


文字列の無効なnull許容構文を削除し、デフォルトのパラメーター値を使用するように更新されました。


6
stringnullにすることはできません。)
Josh M.

23
文字列はnullにすることができます。実際、それはすでにnull可能ですが、「?」は必要ありません。
ProfK 2011

9
@ProfK-いいえ、文字列はnullの可能性がある参照型です。「ヌル可能」ではありません。Nullableは、Nullable <T>(つまりT?)を使用していることを意味します。ジョシュのポイントはあなたが置くことができないということです?文字列の後。これは値の型ではないため、Nullable <T>は値の型のみを受け入れます。
エリックファンケンブッシュ

4
私はランダムにこの質問に戻る道を見つけ、それから上のコメントを投稿したことに気付きました。これの記憶はありません...奇妙です!それがstringできないことはまだ本当nullableです。しかしそれは可能nullです!どちらにしても、最初のコメントは誠意をもって投稿しました。
ジョシュ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」アクションが呼び出され、「Customer / LoadCustomerByName」というURL構造で「LoadCustomer(string str)」が呼び出されます。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

私がこのコードプロジェクトの記事から取った上記の答え-> MVCアクションのオーバーロード


これをありがとう。属性を使用するのではなく、最初から別のアクション名を使用してもよいと思います。
ダン

1
@Danですが、C#側にはポリモーフィズムがありません。
Shivprasad Koirala、2015

正解です。コントローラーメソッドのオーバーロードはありませんが、HTTPとは関係ありません。
チョーキー

説明をありがとう。+1。C#ではなく、より多くのHTTPを考える必要があります。OO戦略でアクションにアプローチする理由はありません。

15

この問題を克服するには、各アクションのを調べ、投稿されたフォーム値と比較し、フォーム値が一致しないメソッドを拒否する記述します(もちろん、ボタン名は除きます)。ActionMethodSelectorAttributeMethodInfo

次に例を示します。- http : //blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

しかし、これは良い考えではありません。


@Cerbrusは恐ろしいハックであり、コントローラのコードを次に見る人が非常に非標準的なアプローチに混乱するためです。
Ian Mercer

ふふふふ。
Cerbrus

14

私が知る限り、異なるhttpメソッドを使用する場合、同じメソッドしか使用できません。

すなわち

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

}

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

}

2
装飾はオーバーロードとは関係ありません。オーバーロードを許可するパラメーターリストです。
スカイサンダーズ

@SkySanders同意しません。パラメーターベースのオーバーロードはMVCコントローラーメソッドでは機能しません-動作例はありますか?乾杯。
チョーキー

[HttpPost]代わりに属性を使用します[AcceptVerbs("POST")]
フレッド

9

MVC5 の属性ルーティングを使用してこれを実現しました。確かに、私はWebFormsを使用した10年間の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)
{
    //.....
}

これが誰かを助け、誰かを間違った道に導いていないことを願っています。:-)


よくやった!私はこの問題に遭遇しました、あなたは私を救いました!私はまた、Webフォームを "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 2013

それはしばらくの間ですが、MVCが一致するシグで2つの個別のメソッドを区別していなかったため、これを行ったと思います。私は他の方法でHTTPGETを入れていなかったのに、私は.. HttpPost属性を使用していた
DevDave

@DevDaveおよび両方のメソッドに起因するものである場合は、system.web.mvcの属性ではなく、system.web.mvcの属性を使用していることを確認してください。
チョーキー

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、。これは、最初と2番目のIndexアクションにそれぞれマップされます。

このサンプルコントローラーを調べると、上記のデフォルトのルートパターンを超えていることがわかります。デフォルトは、URL構造がコードの命名規則と完全に一致する場合に適切に機能しますが、常にそうであるとは限りません。コードはドメインを説明するものである必要がありますが、URLのコンテンツはSEO要件などの他の基準に基づいている必要があるため、URLは多くの場合、さらに進む必要があります。

デフォルトのルーティングパターンの利点は、一意のルートが自動的に作成されることです。URLは一意のコントローラータイプとメンバーに一致するため、これはコンパイラーによって強制されます。独自のルートパターンを展開するには、一意性を確保し、それらが機能するように慎重に検討する必要があります。

重要な注意点 1つの欠点は、ルーティングを使用してオーバーロードされたアクションのURLを生成することが、UrlHelper.Actionを使用する場合など、アクション名に基づく場合は機能しないことです。ただし、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

私のアプリケーションでも同じ問題に直面しています。メソッド情報を変更せずに、アクションヘッドに[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()

編集:これは明らかに、オーバーライドメソッドがOPの意図ではないように見える派生クラスにある場合にのみ適用されます。


2
あなたはおそらくその質問を誤解しているでしょう。OPは、派生クラスでオーバーライドするのではなく、同じコントローラーでメソッドをオーバーロードすることを求めています。
エース

@Andiih:両方のメソッドが同じコントローラーにある場合はどうなりますか?
Dharmik Bhandari、2012


0

各コントローラーメソッドに許可されているパブリックシグネチャは1つだけです。オーバーロードしようとすると、コンパイルされますが、経験したランタイムエラーが発生します。

別の動詞を使用したくない場合は([HttpGet]および[HttpPost]属性など)を使用してオーバーロードされたメソッド(機能する)を区別したり、ルーティングを変更したりしたくない場合は、別の名前の別のメソッドを提供するか、または既存のメソッド内でディスパッチします。ここに私がそれをした方法があります:

以前、下位互換性を維持しなければならない状況に陥りました。元のメソッドは2つのパラメーターを想定していましたが、新しいメソッドには1つしかありませんでした。MVCがエントリポイントを見つけられなくなったため、期待した方法でオーバーロードしても機能しませんでした。

それを解決するために、私は次のことをしました:

  1. 2つのオーバーロードされたアクションメソッドをパブリックからプライベートに変更
  2. 「ただ」2つの文字列パラメータを含む1つの新しいパブリックメソッドを作成しました。それはディスパッチャーとして機能しました、すなわち:

    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には1つだけ必要です。


0

遅れて申し訳ありません。私は同じ問題を抱えていて、良い答えとのリンクを見つけました、それは新しい人を助けることができますか

BinaryIntellect Webサイトと作成者のすべてのクレジット

基本的には、4つの状況があります:貴様の動詞を使用してルーティング使用して過負荷属性[NOACTION]でマーキングし、[ACTIONNAME]とのaction属性名を変更します

だから、それはあなたの要件とあなたの状況に依存します。

ただし、リンクをクリックしてください:

リンク:http : //www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx


-1

これが、異なるモデルの複数のアクションにPOSTする複数のビューに対して1つのGETアクションを使用する試みである場合、最初のGETにリダイレクトするPOSTアクションごとにGETアクションを追加して、更新時に404が発生しないようにします。

ロングショットですが、一般的なシナリオです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.