Bạn có thể quá tải các phương thức điều khiển trong ASP.NET MVC không?


327

Tôi tò mò muốn xem liệu bạn có thể quá tải các phương thức điều khiển trong ASP.NET MVC không. Bất cứ khi nào tôi thử, tôi nhận được lỗi dưới đây. Hai phương thức chấp nhận các đối số khác nhau. Đây có phải là một cái gì đó không thể được thực hiện?

Yêu cầu hiện tại cho hành động 'MyMethod' trên loại trình điều khiển 'MyContoder' không rõ ràng giữa các phương thức hành động sau:


10
@andy cũng giống như vậy đối với mvc 4 :)
basarat

10
Và tương tự cho mvc 5
DhruvJoshi

10
Và tương tự cho mvc 6
Imad

7
Và tương tự cho MVC Core 1.1
kall2sollies

7
Và tương tự cho MVC Core 2.0
Guilherme

Câu trả lời:


201

Bạn có thể sử dụng thuộc tính nếu bạn muốn mã của mình thực hiện quá tải.

[ActionName("MyOverloadedName")]

Nhưng, bạn sẽ phải sử dụng một tên hành động khác cho cùng một phương thức http (như những người khác đã nói). Vì vậy, nó chỉ là ngữ nghĩa tại thời điểm đó. Bạn có muốn có tên trong mã của bạn hoặc thuộc tính của bạn?

Phil có một bài viết liên quan đến vấn đề này: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx


5
Nhược điểm chính của việc sử dụng điều này và làm quá tải hành động của bạn là nó không còn có thể được hiển thị bởi cùng một tệp xem.
Jeff Martin

66
Trên thực tế, nó vẫn có thể hiển thị cùng một tệp xem. Bạn chỉ cần xác định tên của chế độ xem thay vì gọi một cách mù quáng return View();. Ví dụ : return View("MyOverloadedName");.
EAMann

1
@JD nhưng Microsoft nói .. Một phương thức được sử dụng như một hành động của bộ điều khiển không thể bị quá tải .. Bạn có thể xem nó ở đây .. asp.net/mvc/tutorials/controllers-and-routing/
himanshupareek66

@EAMann Nice, tôi luôn xác định toàn bộ đường dẫn cho chế độ xem cho đến tận bây giờ
Alexander Derck

69

Đúng. Tôi đã có thể làm điều này bằng cách thiết lập HttpGet/ HttpPost(hoặc tương đương AcceptVerbsthuộc tính) cho từng phương pháp điều khiển để một cái gì đó khác biệt, nghĩa là, HttpGethoặc HttpPost, nhưng không phải cả hai. Bằng cách đó, nó có thể cho biết dựa trên loại yêu cầu sử dụng phương thức nào.

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

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

Một gợi ý tôi có là, trong trường hợp như thế này, sẽ có một triển khai riêng mà cả hai phương thức Hành động công khai của bạn đều dựa vào để tránh trùng lặp mã.


1
Với MVC2 trở lên, người ta cũng có thể sử dụng thuộc tính HttpPost / HttpGet
yoel halb

@yohal Vâng, đó sẽ là cách hợp quy để xử lý ngay bây giờ nếu bạn không cần hỗ trợ nhiều động từ.
tvanfosson

3
Chỉ cần cẩn thận không lạm dụng điều này để vi phạm các nguyên tắc của REST.
Fred

1
Khá chắc chắn rằng điều này chỉ hoạt động vì Show()phương pháp của bạn có chữ ký khác nhau. Nếu và khi bạn cần gửi thông tin vào phiên bản Nhận, phiên bản Nhận và Đăng của bạn sẽ có cùng chữ ký và bạn cần có ActionNamethuộc tính hoặc một trong các bản sửa lỗi khác được đề cập trong bài đăng này.
Scott Fraley

1
@ ScottK.Fraley đó là sự thật. Nếu họ cần cùng một chữ ký, bạn phải đặt tên khác và áp dụng ActionNameAttribute. Trong thực tế, tôi hiếm khi thấy rằng đó là trường hợp.
tvanfosson

42

Đây là một cái gì đó khác mà bạn có thể làm ... bạn muốn một phương thức có thể có một tham số và không.

Tại sao không thử điều này ...

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

Điều này đã làm việc cho tôi ... và trong phương pháp này, bạn thực sự có thể kiểm tra xem bạn có tham số đến không.


Đã cập nhật để xóa cú pháp nullable không hợp lệ trên chuỗi và sử dụng giá trị tham số mặc định.


6
( stringkhông thể là nullable.)
Josh M.

23
chuỗi có thể là nullable. Trên thực tế, nó đã bị vô hiệu hóa, chỉ là không cần '?'
ProfK

9
@ProfK - Không, chuỗi là loại tham chiếu có thể là null. Nó không phải là "nullable". Nullable có nghĩa là bạn đang sử dụng Nullable <T> (tức là T?). Quan điểm của Josh là bạn không thể đặt? sau chuỗi vì đó không phải là loại giá trị và Nullable <T> chỉ chấp nhận loại giá trị.
Erik Funkenbusch

4
Tôi ngẫu nhiên tìm thấy đường trở lại câu hỏi này và sau đó nhận ra tôi đã đăng bình luận ở trên. Không có hồi ức về điều này ... kỳ lạ! Vẫn đúng là stringkhông thể nullable; nhưng nó có thể được null! Dù bằng cách nào tôi cũng đăng bình luận ban đầu mà không chân thành.
Josh M.

20

Không, Không và Không. Đi và thử mã trình điều khiển bên dưới nơi chúng tôi có "LoadCustomer" bị quá tải.

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

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

Nếu bạn cố gắng gọi hành động "LoadCustomer", bạn sẽ gặp lỗi như trong hình dưới đây.

nhập mô tả hình ảnh ở đây

Đa hình là một phần của lập trình C # trong khi HTTP là giao thức. HTTP không hiểu đa hình. HTTP hoạt động trên khái niệm hoặc URL và URL chỉ có thể có tên duy nhất. Vì vậy, HTTP không thực hiện đa hình.

Để sửa lỗi tương tự, chúng ta cần sử dụng thuộc tính "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");
        }
    }

Vì vậy, bây giờ nếu bạn thực hiện cuộc gọi tới URL "Khách hàng / LoadCustomer", hành động "LoadCustomer" sẽ được gọi và với cấu trúc URL "Khách hàng / LoadCustomerByName", "LoadCustomer (chuỗi str)" sẽ được gọi.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Câu trả lời trên tôi đã lấy từ bài viết về bảng mã này -> Quá tải hành động MVC


Cám ơn vì cái này. Tôi đoán bạn cũng có thể chỉ sử dụng một tên hành động khác ngay từ đầu chứ không sử dụng thuộc tính.
Dan

1
@Dan nhưng sau đó chúng tôi không có tính đa hình ở phía C #.
Shivprasad Koirala

Bạn đã đúng, không có quá tải phương thức điều khiển nhưng không liên quan gì đến HTTP.
Phấn

Cảm ơn đã làm rõ. +1. Nên suy nghĩ nhiều HTTP chứ không phải C #. Không có lý do để tiếp cận các hành động với chiến lược OO.

15

Để khắc phục vấn đề này, bạn có thể viết một bài ActionMethodSelectorAttributekiểm tra MethodInfocho từng hành động và so sánh nó với các giá trị Biểu mẫu đã đăng và sau đó từ chối mọi phương thức mà các giá trị biểu mẫu không khớp (tất nhiên không bao gồm tên nút).

Dưới đây là một ví dụ: - http://blog.abodit.com/2010/02/asp-net-mvc-ambigupt-match/

NHƯNG, đây không phải là một ý tưởng tốt.


"NHƯNG, đây không phải là một ý tưởng tốt." Tại sao không?
Cerbrus

@Cerbrus vì đó là một vụ hack khủng khiếp và người tiếp theo nhìn vào mã điều khiển của bạn sẽ bị nhầm lẫn bởi một cách tiếp cận rất không chuẩn.
Ian Mercer

Heh, đủ công bằng.
Cerbrus

14

Theo tôi biết bạn chỉ có thể có cùng một phương thức khi sử dụng các phương thức http khác nhau.

I E

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

}

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

}

2
các trang trí không có gì để làm với quá tải. nó danh sách các tham số cho phép quá tải.
Sky Sanders

@SkySanders Tôi không đồng ý, quá tải dựa trên tham số không hoạt động trong các phương thức của bộ điều khiển MVC - bạn đã có một ví dụ hoạt động về nó chưa? Chúc mừng.
Phấn

Sử dụng [HttpPost]thuộc tính thay vì [AcceptVerbs("POST")].
Fred

9

Tôi đã đạt được điều này với sự trợ giúp của Định tuyến thuộc tính trong MVC5. Phải thừa nhận rằng tôi chưa quen với MVC đến từ một thập kỷ phát triển web bằng WebForms, nhưng những điều sau đây đã giúp ích cho tôi. Không giống như câu trả lời được chấp nhận, điều này cho phép tất cả các hành động quá tải được hiển thị bởi cùng một tệp xem.

Trước tiên hãy bật Định tuyến thuộc tính trong 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 }
        );            
    }
}

Tùy chọn trang trí lớp trình điều khiển của bạn với tiền tố tuyến mặc định.

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

Sau đó trang trí các hành động điều khiển của bạn làm quá tải lẫn nhau với một lộ trình và thông số chung cho phù hợp. Sử dụng các tham số bị ràng buộc kiểu, bạn có thể sử dụng cùng định dạng URI với ID của các loại khác nhau.

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

Hy vọng điều này sẽ giúp và không dẫn ai đó đi sai đường. :-)


Công việc tốt đẹp! Tôi vừa gặp vấn đề này, bạn đã cứu tôi! Tôi cũng có "x" năm với WebForms - vì vậy vẫn còn rất nhiều đường cong học tập. Không thể có một công việc mà không có MVC ngay bây giờ haha
Tez Wingfield

4

Bạn có thể sử dụng một ActionResultđể đối phó với cả hai PostGet:

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

Hữu ích nếu bạn GetPostphương pháp có chữ ký phù hợp.


1
Hmm, loại - một lần nữa phát minh lại bánh xe, nhưng lần này có hình dạng giống như hình vuông. Tại sao không chỉ đơn giản là sử dụng các thuộc tính [HttpPost / Get]?
Chủ tịch

đã được một thời gian nhưng tôi nghĩ rằng tôi đã làm điều này bởi vì MVC không phân biệt giữa hai phương thức riêng biệt với các phép ghép phù hợp. Tôi đã sử dụng thuộc tính HttpPost, mặc dù tôi đã không đặt HTTPGet vào phương thức khác ..
DevDave

@DevDave cũng như quy kết cả hai phương thức, hãy chắc chắn rằng bạn đang sử dụng các thuộc tính từ system.web.mvc - chứ không phải các thuộc tính từ system.web.http!
Phấn

4

Tôi vừa gặp câu hỏi này và, mặc dù bây giờ nó đã khá cũ, nhưng nó vẫn rất phù hợp. Trớ trêu thay, một bình luận chính xác trong chủ đề này đã được đăng bởi một người mới bắt đầu tự thú nhận về MVC khi anh ấy viết bài đăng. Ngay cả các tài liệu ASP.NET cũng không hoàn toàn chính xác. Tôi có một dự án lớn và tôi quá tải thành công các phương thức hành động.

Nếu một người hiểu định tuyến, ngoài mẫu tuyến đường mặc định {bộ điều khiển} / {hành động} / {id} đơn giản, có thể rõ ràng rằng các hành động của bộ điều khiển có thể được ánh xạ bằng bất kỳ mẫu duy nhất nào. Một người nào đó ở đây đã nói về đa hình và nói: "HTTP không hiểu đa hình", nhưng định tuyến không liên quan gì đến HTTP. Nó chỉ đơn giản là một cơ chế để khớp mẫu chuỗi.

Cách tốt nhất để thực hiện công việc này là sử dụng các thuộc tính định tuyến, ví dụ:

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

Các hành động này sẽ chăm sóc các url như /cars/usa/new-york/cars/usa/texas/dallas, sẽ ánh xạ tới các hành động Index đầu tiên và thứ hai tương ứng.

Kiểm tra bộ điều khiển ví dụ này, rõ ràng là nó vượt ra ngoài mẫu tuyến đường mặc định được đề cập ở trên. Mặc định hoạt động tốt nếu cấu trúc url của bạn khớp chính xác với các quy ước đặt tên mã của bạn, nhưng điều này không phải lúc nào cũng đúng. Mã phải được mô tả về tên miền, nhưng các url thường cần phải đi xa hơn vì nội dung của chúng phải dựa trên các tiêu chí khác, chẳng hạn như yêu cầu SEO.

Lợi ích của mẫu định tuyến mặc định là nó tự động tạo các tuyến duy nhất. Điều này được thực thi bởi trình biên dịch vì các url sẽ khớp với các loại và thành viên của trình điều khiển duy nhất. Việc lăn các mẫu tuyến đường của riêng bạn sẽ đòi hỏi sự suy nghĩ cẩn thận để đảm bảo tính độc đáo và chúng hoạt động.

Lưu ý quan trọng Một nhược điểm là việc sử dụng định tuyến để tạo url cho các hành động bị quá tải không hoạt động khi dựa trên tên hành động, ví dụ: khi sử dụng UrlHelper.Action. Nhưng nó hoạt động nếu một người sử dụng các tuyến được đặt tên, ví dụ, UrlHelper.RouteUrl. Và sử dụng các tuyến đường được đặt tên là, theo các nguồn được tôn trọng, cách nào cũng được ( http://haacked.com/archive/2010/11/21/named-routes-to-the-resTHER.aspx/ ).

Chúc may mắn!


3

Bạn có thể sử dụng [ActionName ("NewActionName")] để sử dụng cùng một phương thức với một tên khác:

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

Tôi cần quá tải cho:

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

Có một vài tranh luận đủ để tôi kết thúc việc này:

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

Đó không phải là một giải pháp hoàn hảo, đặc biệt nếu bạn có nhiều tranh luận, nhưng nó hoạt động tốt với tôi.


1

Tôi đã phải đối mặt với vấn đề tương tự trong ứng dụng của tôi quá. Không có Modifiyig bất kỳ thông tin Phương thức nào, tôi đã cung cấp [ActionName ("someMeaningfulName")] trên đầu Hành động. Vấn đề đã được giải quyết

[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

Tạo phương thức cơ sở dưới dạng ảo

public virtual ActionResult Index()

Tạo phương thức ghi đè dưới dạng ghi đè

public override ActionResult Index()

Chỉnh sửa: Điều này rõ ràng chỉ áp dụng nếu phương thức ghi đè nằm trong lớp dẫn xuất dường như không phải là ý định của OP.


2
Có lẽ bạn đang hiểu nhầm câu hỏi. OP đang hỏi về việc quá tải phương thức trong cùng một bộ điều khiển, không ghi đè nó trong một lớp dẫn xuất.
Ace

@Andiih: điều gì sẽ xảy ra nếu cả hai phương thức nằm trong cùng một bộ điều khiển?
Dharmik Bhandari


0

Chỉ có một chữ ký công khai được phép cho mỗi phương thức điều khiển. Nếu bạn cố gắng quá tải nó, nó sẽ biên dịch, nhưng bạn đang gặp phải lỗi thời gian chạy mà bạn gặp phải.

Nếu bạn không sẵn sàng sử dụng các động từ khác nhau (như thuộc tính [HttpGet][HttpPost]thuộc tính) để phân biệt các phương thức bị quá tải (sẽ hoạt động) hoặc thay đổi định tuyến, thì điều còn lại là bạn có thể cung cấp một phương thức khác với một tên khác hoặc bạn có thể công văn bên trong của phương thức hiện có. Đây là cách tôi đã làm:

Tôi đã từng rơi vào tình huống phải duy trì khả năng tương thích ngược. Phương thức ban đầu dự kiến ​​hai tham số, nhưng phương thức mới chỉ có một. Quá tải theo cách tôi mong đợi đã không hoạt động vì MVC không tìm thấy điểm vào nữa.

Để giải quyết điều đó, tôi đã làm như sau:

  1. Đã thay đổi 2 phương thức hành động quá tải từ công khai sang riêng tư
  2. Tạo một phương thức công khai mới chứa "chỉ" 2 tham số chuỗi. Cái đó đóng vai trò là người điều phối, tức là:

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

Tất nhiên, đây là một hack và nên được tái cấu trúc sau. Nhưng hiện tại, nó đã làm việc cho tôi.

Bạn cũng có thể tạo một bộ điều phối như:

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

Bạn có thể thấy, UpdateAction cần 2 tham số, trong khi DeleteAction chỉ cần một tham số.


0

Xin lỗi về sự chậm trễ. Tôi đã có cùng một vấn đề và tôi tìm thấy một liên kết với câu trả lời tốt, điều đó có thể giúp những người mới

Tất cả các khoản tín dụng cho trang web BinaryIntellect và các tác giả

Về cơ bản, có bốn tình huống: sử dụng động từ differents , sử dụng định tuyến , đánh dấu quá tải bằng thuộc tính [NoAction]thay đổi tên thuộc tính hành động bằng [ActionName]

Vì vậy, phụ thuộc vào đó là yêu cầu của bạn và tình hình của bạn.

Dù sao, hãy theo liên kết:

Liên kết: http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx


-1

Nếu đây là một nỗ lực sử dụng một hành động GET cho một số chế độ xem POST cho một số hành động với các mô hình khác nhau, thì hãy thử thêm hành động GET cho mỗi hành động POST chuyển hướng đến GET đầu tiên để ngăn 404 khi làm mới.

Cú sút xa nhưng kịch bản chung.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.