web-dev-qa-db-ja.com

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

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

コントローラタイプ「MyController」でのアクション「MyMethod」の現在の要求は、次のアクションメソッド間であいまいです。

316
Papa Burgundy

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

[ActionName("MyOverloadedName")]

ただし、他の人が言っているように、同じhttpメソッドに対して異なるアクション名を使用する必要があります。そのため、その時点でのセマンティクスにすぎません。コードや属性に名前を入れたいですか?

Philにはこれに関連する記事があります。 http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

199
JD Conley

はい。これを行うには、各コントローラーメソッドのHttpGet/HttpPost(または同等のAcceptVerbs属性)を別の何か、つまりHttpGetまたはHttpPostに設定しますが、両方に設定することはできません。そうすれば、リクエストのタイプに基づいて、どのメソッドを使用するかを判断できます。

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

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

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

69
tvanfosson

他にもできることがあります。パラメータを持ち、持たないメソッドが必要です。

これを試してみてください...

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

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


41
Farrel

いいえ、いいえ、いいえ。「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」アクションを呼び出そうとすると、次の図に示すようにエラーが発生します。

enter image description here

ポリモーフィズムは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)」が呼び出されます。

enter image description here

enter image description here

上記の回答は、このコードプロジェクトの記事から取ったものです-> MVC Action overloading

20

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

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

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

15
Ian Mercer

私の知る限り、異なるHTTPメソッドを使用する場合にのみ同じメソッドを使用できます。

つまり.

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

}

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

}
13
keeney

MVC5の Attribute Routing を使用してこれを達成しました。確かに、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)
{
    //.....
}

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

9
cookdn

私はこの質問に出くわしましたが、今ではかなり古いものですが、それでも非常に重要です。皮肉なことに、このスレッドの1つの正しいコメントは、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
    }
}

これらのアクションは、/cars/usa/new-york/cars/usa/texas/dallasなどのURLを処理します。これらのURLは、それぞれ最初と2番目のインデックスアクションにマッピングされます。

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

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

重要な注意1つの欠点は、ルーティングを使用して、オーバーロードされたアクションのURLを生成することは、アクション名に基づいて、たとえばUrlHelper.Actionを使用するとき、機能しないことです。ただし、名前付きルート(UrlHelper.RouteUrlなど)を使用する場合は機能します。そして、よく知られているソースによると、名前付きルートを使用することは、とにかく行く方法です( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ )。

がんばろう!

4
DvS

単一のActionResultを使用して、PostGetの両方を処理できます。

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

GetメソッドとPostメソッドに一致するシグネチャがある場合に役立ちます。

4
DevDave

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

オーバーロードが必要でした:

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

特に多くの議論がある場合、それは完璧な解決策ではありませんが、私にはうまくいきます。

2
Kasey Speakman

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

        }
1
ಅನಿಲ್

各コントローラーメソッドに許可されるパブリック署名は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
Matt

基本メソッドを仮想として作成します

public virtual ActionResult Index()

オーバーライドされたメソッドをオーバーライドとして作成します

public override ActionResult Index()

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

0
Andiih

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

BinaryIntellect Webサイトおよび著者のすべてのクレジット

基本的に、4つの状況があります:異なる動詞を使用するルーティングを使用する過負荷マーキングと[NoAction ]属性およびアクション属性名を[ActionName]で変更

ですから、それはあなたの要求と状況です。

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

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

0
Eric Saboia

別のスレッドに投稿されたこの回答が好きです

これは主に、別のコントローラーから継承し、ベースコントローラーからのアクションをオーバーライドする場合に使用されます

ASP.NET MVC-異なるパラメーターでアクションをオーバーライドする

0
Keyjote