ASP.NET MVC'de denetleyici yöntemlerini aşırı yükleyebilir misiniz?


327

ASP.NET MVC'de denetleyici yöntemlerini aşırı yükleyip yükleyemeyeceğinizi merak ediyorum. Her denediğimde aşağıdaki hatayı alıyorum. İki yöntem farklı argümanları kabul eder. Bu yapılamayacak bir şey mi?

'MyController' denetleyici türündeki 'MyMethod' eylemi için geçerli istek, aşağıdaki eylem yöntemleri arasında belirsiz:


10
@andy mvc 4 için de aynı :)
basarat

10
Ve aynı mvc 5 için
DhruvJoshi

10
Ve aynı mvc 6 için
Imad

7
Ve aynı MVC Core 1.1 için
kall2sollies

7
MVC Core 2.0
Guilherme

Yanıtlar:


201

Kodunuzun aşırı yüklenmesini istiyorsanız bu özelliği kullanabilirsiniz.

[ActionName("MyOverloadedName")]

Ancak, aynı http yöntemi için (diğerlerinin söylediği gibi) farklı bir işlem adı kullanmanız gerekir. Yani bu noktada sadece anlambilim. Kodunuzda veya özelliğinizde adı kullanmayı tercih eder misiniz?

Phil'in bununla ilgili bir makalesi var: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx


5
Bunu kullanmanın ve eyleminizi aşırı yüklemenin ana düşüşü, artık aynı görünüm dosyası tarafından oluşturulamamasıdır.
Jeff Martin

66
Aslında, aynı görünüm dosyasını yine de oluşturabilir. Körü körüne aramak yerine görünümün adını belirtmeniz yeterlidir return View();. Örneğin: return View("MyOverloadedName");.
EAMann

1
@JD ama Microsoft diyor ki .. Denetleyici eylemi olarak kullanılan bir yöntem aşırı yüklenemez .. Burada görebilirsiniz .. asp.net/mvc/tutorials/controllers-and-routing/…
himanshupareek66

@EAMann Nice, şimdiye kadar her zaman görüş için tüm yolu tanımlıyordum
Alexander Derck

69

Evet. Bunu , her denetleyici yöntemi için HttpGet/ HttpPost(veya eşdeğer AcceptVerbsözniteliği) farklı bir şeye, yani HttpGetveya HttpPosther ikisine birden ayarlayarak ayarlayabildim . Bu şekilde hangi yöntemin kullanılacağını talep türüne göre söyleyebilir.

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

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

Sahip olduğum bir öneri, böyle bir durum için, ortak eylem yöntemlerinizin her ikisinin de kod çoğaltılmasını önlemek için güvendiği özel bir uygulamaya sahip olacağıdır.


1
MVC2 ve üstü ile HttpPost / HttpGet özniteliği de kullanılabilir
yoel halb

@yohal Evet, birden fazla fiili desteklemeniz gerekmiyorsa, şimdi bunu yapmanın kanonik yolu olurdu.
tvanfosson

3
Sadece REST ilkelerini ihlal etmek için bunu kötüye kullanmamaya dikkat edin.
Fred

1
Bunun işe yaradığından eminim çünkü Show()yöntemlerinizin farklı imzaları var. Get sürümüne bilgi göndermeniz gerekiyorsa ve alındığında, Get ve Post sürümleriniz aynı imzayla ActionNamesonuçlanır ve bu yayında belirtilen özelliğe veya diğer düzeltmelerden birine ihtiyacınız olur .
Scott Fraley

1
@ ScottK.Fraley bu doğru. Aynı imzayı gerektiriyorlarsa, bunları farklı bir şekilde adlandırmanız ve uygulamanız gerekir ActionNameAttribute. Uygulamada nadiren durum böyle buldum.
tvanfosson

42

İşte yapabileceğiniz başka bir şey ... bir parametreye sahip olan ve olmayan bir yöntem istiyorsunuz.

Neden denemiyorsun ...

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

Bu benim için çalıştı ... ve bu yöntemde, aslında gelen parametreye sahip olup olmadığınızı görmek için test edebilirsiniz.


Dizede geçersiz null olabilecek sözdizimini kaldırmak ve varsayılan bir parametre değeri kullanmak için güncellendi.


6
( stringiptal edilemez.)
Josh M.

23
dize boş olabilir. Aslında, zaten geçersizdir, sadece '?'
ProfK

9
@ProfK - Hayır, dize null olabilecek bir başvuru türüdür. "Sıfırlanabilir" değil. Nullable, Nullable <T> (yani T?) Kullandığınız anlamına gelir. Josh'un anlamı, onu koyamayacağın mı? bir değer türü olmadığı için Nullable <T> yalnızca değer türlerini kabul ettiği için dizeden sonra.
Erik Funkenbusch

4
Bu soruya rastgele geri dönüş yolumu buldum ve sonra yukarıdaki yorumu gönderdiğimi fark ettim. Bunu hatırlamıyorum ... tuhaf! Hala doğrudur stringteneke olamaz nullable; ama olabilir null! Her iki şekilde de ilk yorumu samimiyet olmadan yayınladım.
Josh M.

20

Hayır, Hayır ve Hayır. "LoadCustomer" ı aşırı yüklediğimiz aşağıdaki denetleyici kodunu deneyin.

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

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

"LoadCustomer" eylemini çağırmaya çalışırsanız, aşağıdaki şekilde gösterildiği gibi hata alırsınız.

resim açıklamasını buraya girin

Çok biçimlilik C # programlamanın bir parçasıdır, HTTP ise bir protokoldür. HTTP polimorfizmi anlamıyor. HTTP, kavram veya URL üzerinde çalışır ve URL yalnızca benzersiz adlara sahip olabilir. Dolayısıyla HTTP polimorfizm uygulamıyor.

Aynı sorunu gidermek için "ActionName" özelliğini kullanmamız gerekiyor.

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

Şimdi "Müşteri / LoadCustomer" URL'sine bir çağrı yaparsanız, "LoadCustomer" eylemi çağrılır ve "Customer / LoadCustomerByName" URL yapısı ile "LoadCustomer (string str)" çağrılır.

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Yukarıdaki cevap bu codeproject makaleden aldım -> MVC Eylem aşırı yükleme


Bunun için teşekkürler. Sanırım özniteliği kullanmak yerine en başından farklı bir işlem adı da kullanabilirsiniz.
Dan

1
@ Dan ama sonra C # tarafında polimorfizm yok.
Shivprasad Koirala

Doğru, aşırı yükleyici denetleyici yöntemi yok ama HTTP ile ilgisi yok.
Kireçli

Açıklama için teşekkürler. +1. Daha fazla HTTP düşünmeli ve C # değil. Eylemlere OO stratejisi ile yaklaşmak için hiçbir neden yoktur.

15

Bu sorunu aşmak için olabilir bir yazma ActionMethodSelectorAttributeincelerMethodInfo her işlem için ve nakledilen Formu değerlerine karşılaştırır ve sonra (tabii, düğme adı hariç) form değerleri eşleşmiyor kendisi için herhangi bir yöntem reddeder.

İşte bir örnek: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

AMA, bu iyi bir fikir değil.


@Cerbrus çünkü korkunç bir hack ve denetleyici kodunuza bakan bir sonraki kişi çok standart dışı bir yaklaşımla karıştırılacak.
Ian Mercer

Heh, yeterince adil.
Cerbrus

14

Bildiğim kadarıyla sadece farklı http yöntemleri kullanırken aynı yöntemi olabilir.

yani

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

}

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

}

2
dekorasyonların aşırı yük ile ilgisi yoktur. aşırı yüklemeye izin veren parametre listesi.
Sky Sanders

@SkySanders Kabul etmiyorum, parametrelere dayalı aşırı yükleme MVC denetleyici yöntemlerinde çalışmıyor - bunun çalışan bir örneği var mı? Şerefe.
Kireçli

Kullanım [HttpPost]yerine niteliğini [AcceptVerbs("POST")].
Fred

9

Bunu MVC5'teki Öznitelik Yönlendirmesi sayesinde başardım. Kuşkusuz MVC'de WebForms kullanarak on yıllık bir web geliştirme sürecinden yeni geliyorum, ancak aşağıdakiler benim için çalıştı. Kabul edilen cevabın aksine, bu, aşırı yüklenmiş tüm eylemlerin aynı görünüm dosyası tarafından oluşturulmasına izin verir.

Öncelikle App_Start / RouteConfig.cs içinde Öznitelik Yönlendirmeyi etkinleştirin.

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

İsteğe bağlı olarak denetleyici sınıfınızı varsayılan bir rota önekiyle süsleyin.

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

Ardından, birbirlerine aşırı yüklenen denetleyici eylemlerinizi ortak bir yol ve uygun parametrelerle süsleyin. Tür kısıtlamalı parametreleri kullanarak, farklı türdeki kimliklerle aynı URI biçimini kullanabilirsiniz.

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

Umarım bu yardımcı olur ve birini yanlış yola yönlendirmez. :-)


İyi iş! Bu konuya yeni girdim, beni kurtardın! Ayrıca WebForms ile "x" yıl var - bu yüzden hala bir öğrenme eğrisi. MVC'siz bir iş alamıyorum haha
Tez Wingfield

4

Her ActionResultikisini de ele almak için bir single kullanabilirsiniz Postve Get:

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

Faydalı senin eğer Getve Postyöntemler eşleşen imzaları var.


1
Hmm, tekerleği tekrar icat eden, ama bu sefer kare şeklinde. Neden sadece [HttpPost / Get] özelliklerini kullanmıyorsunuz?
SOReader

uzun zaman oldu ama bence bunu yaptım çünkü MVC eşleşen sigs ile iki ayrı yöntem arasında ayrım değildi. HttpGet özelliğini diğer yönteme koymama rağmen HttpPost özniteliğini kullanıyordum ..
DevDave

@DevDave ve her iki yöntemi de ilişkilendirirken, system.web.http ile değil, system.web.mvc'den öznitelikler kullandığınızdan emin olun!
Chalky

4

Ben sadece bu soruya rastladım ve şimdi oldukça eski olmasına rağmen, hala çok alakalı. İronik olarak, bu konudaki doğru bir yorum, yazıyı yazarken MVC'de kendinden itiraf eden bir acemi tarafından yayınlandı. ASP.NET belgeleri bile tamamen doğru değildir. Büyük bir projem var ve eylem yöntemlerini başarıyla aşırı yüklüyorum.

Yönlendirme anlaşılırsa, basit {controller} / {action} / {id} varsayılan rota modelinin ötesinde, denetleyici eylemlerinin herhangi bir benzersiz desen kullanılarak eşleştirilebileceği açık olabilir. Burada birisi polimorfizm hakkında konuştu ve şöyle dedi: "HTTP polimorfizmi anlamıyor", ancak yönlendirmenin HTTP ile ilgisi yoktur. Basitçe söylemek gerekirse, dize deseni eşleşmesi için bir mekanizmadır.

Bu işi yapmanın en iyi yolu, yönlendirme niteliklerini kullanmaktır, örneğin:

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

Bu eylemler, sırasıyla birinci ve ikinci Dizin işlemleriyle eşleşecek /cars/usa/new-yorkve gibi URL'lerle ilgilenir /cars/usa/texas/dallas.

Bu örnek denetleyicinin incelenmesi, yukarıda belirtilen varsayılan rota modelinin ötesine geçtiği açıktır. URL yapınız kod adlandırma kurallarınızla tam olarak eşleşiyorsa varsayılan değer iyi çalışır, ancak bu her zaman böyle değildir. Kod etki alanını tanımlayıcı olmalıdır, ancak içeriği SEO gereksinimleri gibi diğer ölçütlere dayandığından, URL'lerin genellikle daha ileri gitmesi gerekir.

Varsayılan yönlendirme düzeninin yararı, otomatik olarak benzersiz yollar oluşturmasıdır. URL'ler benzersiz denetleyici türleriyle ve üyelerle eşleşeceğinden bu derleyici tarafından uygulanır. Kendi rota modellerinizi yuvarlamak, benzersizliği ve bunların çalışmasını sağlamak için dikkatli bir düşünce gerektirir.

Önemli not Tek dezavantajı, aşırı yüklenmiş eylemler için URL oluşturmak üzere yönlendirme kullanmanın, bir eylem adına dayanıldığında, örneğin UrlHelper.Action kullanırken işe yaramamasıdır. Ancak, adlandırılmış yollar kullanırsa çalışır, örneğin UrlHelper.RouteUrl. Ve adlandırılmış yolları kullanmak, saygın kaynaklara göre, yine de gitmenin yoludur ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

İyi şanslar!


3

Aynı yöntemi farklı bir adla kullanmak için [ActionName ("NewActionName")] kullanabilirsiniz:

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

Aşağıdakiler için aşırı yüklenmeye ihtiyacım vardı:

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

Sonunda bunu yaparken yeterince az argüman vardı:

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

Bu, özellikle çok fazla tartışmanız varsa, mükemmel bir çözüm değil, ama benim için iyi çalışıyor.


1

Başvurumda da aynı sorunla karşılaştım. Modifiyig herhangi bir Yöntem bilgi olmadan, Eylem kafasına [ActionName ("SomeMeaningfulName")] sağladım. sorun çözüldü

[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

Temel yöntemi sanal olarak oluşturma

public virtual ActionResult Index()

Geçersiz kılma yöntemini geçersiz kılma olarak oluşturma

public override ActionResult Index()

Düzenleme: Bu açıkça yalnızca geçersiz kılma yöntemi OP'nin amacı değil gibi türetilmiş bir sınıfta varsa geçerlidir.


2
Muhtemelen soruyu yanlış anlıyorsunuz. OP, aynı denetleyicide yöntemin aşırı yüklenmesi, türetilmiş bir sınıfta geçersiz kılınmasını istemiyor.
Ace

@Andiih: Her iki yöntem de aynı denetleyicide olursa ne olur?
Dharmik Bhandari


0

Her denetleyici yöntemi için yalnızca bir genel imzaya izin verilir. Aşırı yüklemeye çalışırsanız, derlenir, ancak yaşadığınız çalışma zamanı hatasını alırsınız.

Farklı fiiller kullanmak istemiyorsanız ( [HttpGet]ve gibi[HttpPost]Aşırı yüklenmiş yöntemleri (işe yarayacak) farklılaştırmak veya yönlendirmeyi değiştirmek için öznitelikler , geriye kalan şey, farklı bir ada sahip başka bir yöntem sağlayabilmeniz veya varolan yöntemin içinde gönderme. İşte böyle yaptım:

Bir zamanlar geriye dönük uyumluluğu korumak zorunda kaldığım bir duruma girdim. Orijinal yöntem iki parametre bekledi, ancak yenisinde sadece bir parametre vardı. Beklediğim şekilde aşırı yükleme işe yaramadı çünkü MVC artık giriş noktasını bulamadı.

Bunu çözmek için aşağıdakileri yaptım:

  1. Aşırı yüklenmiş 2 eylem yöntemini halktan özelliğe değiştirdi
  2. "Just" 2 string parametrelerini içeren yeni bir genel yöntem oluşturuldu. Bu bir dağıtım görevlisi gibi davrandı, yani:

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

Tabii ki, bu bir hack ve daha sonra yeniden düzenlenmelidir. Ama şimdilik benim için çalıştı.

Ayrıca aşağıdakiler gibi bir dağıtım programı da oluşturabilirsiniz:

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

DeleteAction'ın sadece bir parametreye ihtiyaç duyarken, UpdateAction'ın 2 parametreye ihtiyacı olduğunu görebilirsiniz.


0

Gecikme için üzgünüm. Aynı sorunla ilgiliydim ve iyi yanıtlar içeren bir bağlantı buldum, bu yeni adamlara yardımcı olabilir mi

BinaryIntellect web sitesi ve yazarlar için tüm krediler

Temel olarak, dört durum vardır değişik fiilleri kullanarak , yönlendirme kullanılarak , aşırı [NoAction] niteliğiyle işaretleme ve [ActionName] ile action özelliği adını değiştirmek

Yani, bu sizin gereksinimlerinize ve durumunuza bağlıdır.

Ancak, bağlantıyı takip edin:

Bağlantı: http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx


-1

Bu, farklı modellerle çeşitli eylemlere POST gönderen çeşitli görünümler için bir GET eylemini kullanma girişimiyse, yenilemede 404'ü önlemek için ilk GET'e yönlendiren her POST işlemi için bir GET eylemi eklemeyi deneyin.

Uzun atış ama ortak senaryo.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.