Bisakah Anda kelebihan metode pengontrol di ASP.NET MVC?

327

Saya ingin tahu apakah Anda dapat membebani metode pengontrol di ASP.NET MVC. Setiap kali saya mencoba, saya mendapatkan kesalahan di bawah ini. Dua metode menerima argumen yang berbeda. Apakah ini sesuatu yang tidak bisa dilakukan?

Permintaan tindakan saat ini 'MyMethod' pada tipe pengontrol 'MyController' adalah ambigu antara metode tindakan berikut:

Papa Burgundy
sumber
10
@andy juga sama untuk mvc 4 :)
basarat
10
Dan sama untuk mvc 5
DhruvJoshi
10
Dan sama untuk mvc 6
Imad
7
Dan sama untuk MVC Core 1.1
kall2sollies
7
Dan sama untuk MVC Core 2.0
Guilherme

Jawaban:

201

Anda dapat menggunakan atribut jika Anda ingin kode Anda melakukan overloading.

[ActionName("MyOverloadedName")]

Tapi, Anda harus menggunakan nama tindakan yang berbeda untuk metode http yang sama (seperti yang orang lain katakan). Jadi itu hanya semantik pada saat itu. Apakah Anda lebih suka nama dalam kode atau atribut Anda?

Phil memiliki artikel yang berkaitan dengan ini: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

JD Conley
sumber
5
Kelemahan utama menggunakan ini dan membebani tindakan Anda adalah tidak dapat lagi dibuat oleh file tampilan yang sama.
Jeff Martin
66
Sebenarnya, itu masih dapat membuat file tampilan yang sama. Anda hanya perlu menentukan nama tampilan alih-alih panggilan secara membabi buta return View();. Sebagai contoh: return View("MyOverloadedName");.
EAMann
1
@JD tetapi Microsoft mengatakan .. Metode yang digunakan sebagai aksi pengontrol tidak dapat kelebihan beban .. Anda dapat melihatnya di sini .. asp.net/mvc/tutorials/controllers-and-routing/…
himanshupareek66
@ EAMann Bagus, saya selalu mendefinisikan seluruh jalan untuk tampilan sampai sekarang
Alexander Derck
69

Iya. Saya sudah bisa melakukan ini dengan mengatur HttpGet/ HttpPost(atau AcceptVerbsatribut yang setara ) untuk setiap metode pengontrol untuk sesuatu yang berbeda, yaitu, HttpGetatau HttpPost, tetapi tidak keduanya. Dengan begitu ia bisa mengetahui berdasarkan jenis permintaan metode mana yang digunakan.

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

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

Satu saran yang saya miliki adalah bahwa, untuk kasus seperti ini, akan memiliki implementasi pribadi yang bergantung pada kedua metode Tindakan publik Anda untuk menghindari duplikasi kode.

tvanfosson
sumber
1
Dengan MVC2 dan yang lebih baru, Anda juga dapat menggunakan atribut HttpPost / HttpGet
yoel halb
@ Yaohal Ya, itu akan menjadi cara kanonik untuk menanganinya sekarang jika Anda tidak perlu mendukung banyak kata kerja.
tvanfosson
3
Berhati-hatilah untuk tidak menyalahgunakan ini untuk melanggar prinsip-prinsip REST.
Fred
1
Cukup yakin ini hanya berfungsi karena Show()metode Anda memiliki tanda tangan yang berbeda. Jika dan ketika Anda perlu mengirim info ke versi Dapatkan, versi Dapatkan dan Posting Anda kemudian berakhir dengan tanda tangan yang sama, dan Anda akan memerlukan ActionNameatribut atau salah satu perbaikan lain yang disebutkan dalam posting ini.
Scott Fraley
1
@ ScottK.Fraley itu benar. Jika mereka membutuhkan tanda tangan yang sama Anda harus menamainya secara berbeda dan menerapkannya ActionNameAttribute. Dalam praktiknya, saya jarang menemukan itu yang menjadi masalah.
tvanfosson
42

Berikut ini hal lain yang dapat Anda lakukan ... Anda menginginkan metode yang dapat memiliki parameter dan tidak.

Kenapa tidak coba ini ...

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

Ini berhasil bagi saya ... dan dalam metode yang satu ini, Anda benar-benar dapat menguji untuk melihat apakah Anda memiliki parameter yang masuk.


Diperbarui untuk menghapus sintaks nullable yang tidak valid pada string dan menggunakan nilai parameter default.

Farrel
sumber
6
( stringtidak dapat dibatalkan.)
Josh M.
23
string dapat dibatalkan. Bahkan, itu sudah bisa dibatalkan, tidak perlu '?'
ProfK
9
@ProfK - Tidak, string adalah tipe referensi yang bisa nol. Ini bukan "nullable". Nullable berarti Anda menggunakan Nullable <T> (yaitu T?). Maksud Josh adalah Anda tidak bisa mengatakannya? setelah string karena ini bukan tipe nilai, dan Nullable <T> hanya menerima tipe nilai.
Erik Funkenbusch
4
Saya secara acak menemukan jalan saya kembali ke pertanyaan ini dan kemudian menyadari saya memposting komentar di atas. Tidak ada ingatan tentang ini ... aneh! Masih benar bahwa stringtidak mungkin nullable; tapi bisa saja null! Either way saya memposting komentar awal tanpa ketulusan.
Josh M.
20

Tidak, Tidak dan Tidak. Pergi dan coba kode pengontrol di bawah ini di mana kita memiliki "LoadCustomer" kelebihan beban.

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

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

Jika Anda mencoba menjalankan tindakan "LoadCustomer" Anda akan mendapatkan kesalahan seperti yang ditunjukkan pada gambar di bawah ini.

masukkan deskripsi gambar di sini

Polimorfisme adalah bagian dari pemrograman C # sedangkan HTTP adalah sebuah protokol. HTTP tidak mengerti polimorfisme. HTTP berfungsi pada konsep atau URL dan URL hanya dapat memiliki nama unik. Jadi HTTP tidak menerapkan polimorfisme.

Untuk memperbaiki yang sama kita perlu menggunakan atribut "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");
        }
    }

Jadi sekarang jika Anda melakukan panggilan ke URL "Pelanggan / LoadCustomer" tindakan "LoadCustomer" akan dipanggil dan dengan struktur URL "Pelanggan / LoadCustomerByName" "LoadCustomer (string str)" akan dipanggil.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Jawaban di atas telah saya ambil dari artikel proyek ini -> MVC Action overloading

Shivprasad Koirala
sumber
Terima kasih untuk ini. Saya kira Anda mungkin juga hanya menggunakan nama tindakan yang berbeda dari awal daripada menggunakan atribut.
Dan
1
@Bisa tetapi kemudian kita tidak memiliki polimorfisme di sisi C #.
Shivprasad Koirala
Anda benar, tidak ada metode pengontrol yang berlebihan tetapi tidak ada hubungannya dengan HTTP.
Chalky
Terima kasih atas klarifikasi. +1. Seharusnya berpikir lebih banyak HTTP dan bukan C #. Tidak ada alasan untuk mendekati tindakan dengan strategi OO.
15

Untuk mengatasi masalah ini, Anda dapat menulis ActionMethodSelectorAttributeyang memeriksaMethodInfo untuk setiap tindakan dan membandingkannya dengan nilai-nilai Formulir yang diposting dan kemudian menolak metode apa pun yang nilai-nilai formulirnya tidak cocok (tidak termasuk nama tombol, tentu saja).

Berikut ini contohnya: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

TAPI, ini bukan ide yang baik.

Ian Mercer
sumber
@Cerbrus karena ini peretasan yang mengerikan dan orang berikutnya yang melihat kode pengontrol Anda akan bingung oleh pendekatan yang sangat tidak standar.
Ian Mercer
Heh, cukup adil.
Cerbrus
14

Sejauh yang saya tahu Anda hanya dapat memiliki metode yang sama saat menggunakan metode http yang berbeda.

yaitu

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

}

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

}
tajam
sumber
2
dekorasi tidak ada hubungannya dengan kelebihan. itu daftar parameter yang memungkinkan overloading.
Sky Sanders
@SkySanders Saya tidak setuju, overloading berbasis parameter tidak bekerja dalam metode pengontrol MVC - apakah Anda punya contoh yang baik? Bersulang.
Chalky
Gunakan [HttpPost]atribut sebagai ganti [AcceptVerbs("POST")].
Fred
9

Saya telah mencapai ini dengan bantuan Routing Atribut di MVC5. Memang saya baru mengenal MVC yang berasal dari satu dekade pengembangan web menggunakan WebForms, tetapi berikut ini berhasil bagi saya. Berbeda dengan jawaban yang diterima, ini memungkinkan semua tindakan kelebihan diberikan oleh file tampilan yang sama.

Pertama-tama aktifkan Attribute Routing di 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 }
        );            
    }
}

Secara opsional, hiasi kelas pengontrol Anda dengan awalan rute default.

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

Kemudian hiasi tindakan pengontrol Anda yang membebani satu sama lain dengan rute dan parameter umum yang sesuai. Menggunakan parameter tipe terbatas Anda dapat menggunakan format URI yang sama dengan ID dari tipe yang berbeda.

[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)
{
    //.....
}

Semoga ini bisa membantu dan tidak membawa seseorang ke jalan yang salah. :-)

juru masak
sumber
Kerja bagus! Saya baru saja mengalami masalah ini, Anda menyelamatkan saya! Saya juga memiliki "x" tahun dengan WebForms - jadi masih sangat banyak kurva belajar. Tidak bisa mendapatkan pekerjaan tanpa MVC sekarang-a-hari haha
Tez Wingfield
4

Anda dapat menggunakan satu ActionResultuntuk menangani keduanya Postdan Get:

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

Berguna jika Anda Getdan Postmetode memiliki tanda tangan yang cocok.

DevDave
sumber
1
Hmm, jenis-a menciptakan kembali roda lagi, tapi kali ini dalam bentuk seperti persegi. Mengapa tidak menggunakan atribut [HttpPost / Get] saja?
SOReader
sudah lama tapi saya pikir saya melakukan ini karena MVC tidak membedakan antara dua metode yang terpisah dengan sigs yang cocok. Saya menggunakan atribut HttpPost, meskipun saya tidak menempatkan HttpGet pada metode lain ..
DevDave
@DevDave serta mengaitkan kedua metode, pastikan Anda menggunakan atribut dari system.web.mvc - dan bukan yang dari system.web.http!
Chalky
4

Saya baru saja menemukan pertanyaan ini dan, meskipun sudah cukup lama, masih sangat relevan. Ironisnya, satu komentar yang benar di utas ini diposting oleh seorang pemula yang mengaku di MVC ketika ia menulis posting tersebut. Bahkan dokumen ASP.NET tidak sepenuhnya benar. Saya memiliki proyek besar dan saya berhasil membebani metode tindakan.

Jika seseorang memahami perutean, di luar pola rute default {controller} / {action} / {id} sederhana, mungkin jelas bahwa tindakan controller dapat dipetakan menggunakan pola unik apa pun. Seseorang di sini berbicara tentang polimorfisme dan berkata: "HTTP tidak memahami polimorfisme", tetapi perutean tidak ada hubungannya dengan HTTP. Ini, sederhananya, mekanisme untuk pencocokan pola string.

Cara terbaik untuk membuat pekerjaan ini adalah dengan menggunakan atribut perutean, misalnya:

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

Tindakan ini akan mengurus url seperti /cars/usa/new-yorkdan/cars/usa/texas/dallas , yang akan memetakan masing-masing tindakan Indeks pertama dan kedua.

Memeriksa contoh controller ini terbukti bahwa itu melampaui pola rute default yang disebutkan di atas. Default berfungsi dengan baik jika struktur url Anda benar-benar cocok dengan konvensi penamaan kode Anda, tetapi ini tidak selalu terjadi. Kode harus deskriptif dari domain, tetapi url sering harus lebih jauh karena konten mereka harus didasarkan pada kriteria lain, seperti persyaratan SEO.

Manfaat dari pola perutean default adalah secara otomatis membuat rute unik. Ini diberlakukan oleh kompilator karena url akan cocok dengan tipe dan anggota pengontrol yang unik. Menggulirkan pola rute Anda sendiri akan membutuhkan pemikiran yang cermat untuk memastikan keunikan dan bahwa mereka bekerja.

Catatan penting Satu-satunya kelemahan adalah bahwa menggunakan perutean untuk menghasilkan url untuk tindakan yang berlebihan tidak bekerja ketika didasarkan pada nama tindakan, misalnya, ketika menggunakan UrlHelper.Action. Tapi itu berfungsi jika seseorang menggunakan rute bernama, misalnya, UrlHelper.RouteUrl. Dan menggunakan rute bernama, menurut sumber yang dihormati, cara untuk pergi bagaimanapun ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

Semoga berhasil!

DvS
sumber
3

Anda dapat menggunakan [ActionName ("NewActionName")] untuk menggunakan metode yang sama dengan nama yang berbeda:

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");
    }
}
Alex Butenko
sumber
2

Saya membutuhkan kelebihan untuk:

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

Ada beberapa argumen yang cukup di mana saya akhirnya melakukan ini:

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

Itu bukan solusi yang sempurna, terutama jika Anda memiliki banyak argumen, tetapi itu bekerja dengan baik untuk saya.

Kasey Speakman
sumber
1

Saya juga menghadapi masalah yang sama dalam aplikasi saya. Tanpa Modifikasi informasi Metode apa pun, saya telah memberikan [ActionName ("SomeMeaningfulName")] pada Action head. masalah terselesaikan

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

        }
ಅನಿಲ್
sumber
0

Buat metode dasar sebagai virtual

public virtual ActionResult Index()

Buat metode yang diganti sebagai ganti

public override ActionResult Index()

Sunting: Ini jelas hanya berlaku jika metode override dalam kelas turunan yang tampaknya bukan niat OP.

Andiih
sumber
2
Anda mungkin salah paham pertanyaan. OP bertanya tentang overloading metode di controller yang sama, bukan menimpanya dalam kelas turunan.
Ace
@ Andiih: apa yang akan terjadi jika kedua metode berada di pengontrol yang sama?
Dharmik Bhandari
0

Hanya ada satu tanda tangan publik yang diizinkan untuk setiap metode pengontrol. Jika Anda mencoba memuatnya secara berlebihan, itu akan dikompilasi, tetapi Anda mendapatkan kesalahan run-time yang Anda alami.

Jika Anda tidak mau menggunakan kata kerja yang berbeda (seperti [HttpGet]dan[HttpPost] atribut ) untuk membedakan metode kelebihan beban (yang akan bekerja), atau mengubah perutean, maka yang tersisa adalah Anda bisa memberikan metode lain dengan nama yang berbeda, atau Anda bisa kirim dalam metode yang ada. Begini cara saya melakukannya:

Saya pernah datang ke situasi di mana saya harus menjaga kompatibilitas ke belakang. Metode asli mengharapkan dua parameter, tetapi yang baru hanya memiliki satu. Kelebihan cara yang saya harapkan tidak berhasil karena MVC tidak menemukan titik masuk lagi.

Untuk mengatasinya, saya melakukan hal berikut:

  1. Mengubah 2 metode tindakan kelebihan beban dari publik menjadi pribadi
  2. Menciptakan satu metode publik baru yang berisi "hanya" 2 parameter string. Yang itu bertindak sebagai operator, yaitu:

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

Tentu saja, ini adalah retasan dan harus dire-refored nanti. Tetapi untuk saat ini, itu berhasil untuk saya.

Anda juga dapat membuat dispatcher seperti:

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

Anda dapat melihat, bahwa UpdateAction membutuhkan 2 parameter, sedangkan DeleteAction hanya membutuhkan satu parameter.

Mat
sumber
0

Maaf atas keterlambatannya. Saya dengan masalah yang sama dan saya menemukan tautan dengan jawaban yang bagus, dapatkah itu membantu orang baru

Semua kredit untuk situs web BinaryIntellect dan penulisnya

Pada dasarnya, ada empat situasi: menggunakan kata kerja differents , menggunakan perutean , menandai overload dengan atribut [NoAction] dan mengubah nama atribut aksi dengan [ActionName]

Jadi, tergantung itu adalah situasi dan situasi Anda.

Namun, ikuti tautannya:

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

Eric Saboia
sumber
-1

Jika ini merupakan upaya untuk menggunakan satu tindakan GET untuk beberapa tampilan yang POST ke beberapa tindakan dengan model yang berbeda, maka coba tambahkan tindakan GET untuk setiap tindakan POST yang dialihkan ke GET pertama untuk mencegah 404 saat refresh.

Skenario panjang namun umum.

Panos Roditakis
sumber