วิธีการเรียกตัวควบคุมอื่นการกระทำจากตัวควบคุมใน Mvc


153

ฉันต้องการเรียกใช้ตัวควบคุม B การกระทำ FileUploadMsgView จากคอนโทรลเลอร์ A และต้องผ่านพารามิเตอร์ของมัน

 Code---its not going to the controller B's FileUploadMsgView().
    In ControllerA
  private void Test()
    {

        try
        {//some codes here
            ViewBag.FileUploadMsg = "File uploaded successfully.";
            ViewBag.FileUploadFlag = "2";

            RedirectToAction("B", "FileUploadMsgView", new { FileUploadMsg = "File   uploaded successfully" });
        }

     In ControllerB receiving part
  public ActionResult FileUploadMsgView(string FileUploadMsg)
    {
         return View();
    }

3
ฉันรู้ว่าคำถามนี้เก่า แต่ในความเห็นของฉันคุณควรทำเครื่องหมายคำตอบจากโบสถ์เอ็ดว่าดีที่สุดความสัมพันธ์ของแฮ็คก็ยังคงถูกต้อง แต่ทำไมใช้วิธีแก้ปัญหาเมื่อคุณสามารถใช้วิธีที่มันควรจะเป็น และรับผลลัพธ์ที่ต้องการ
Anders M.

1
@AndersM คำตอบของเอ็ดจะเปลี่ยนเส้นทาง นั่นไม่ใช่สิ่งที่ฉันต้องการเมื่อฉันพบคำถามนี้เพื่อค้นหาวิธีแก้ไข
mxmissile

@ XMmissile ไม่ควรเป็นคนหัวเสีย แต่คำตอบของ Ed คือสิ่งที่ผู้ถามต้องการเพราะเขาต้องการมุมมองที่ถูกส่งคืนตามสิ่งที่อัปโหลดฉันยอมรับว่าผู้ถามสามารถทำงานได้ดีขึ้นในการกำหนดคำถามของเขา (นี่เป็นคำที่เหมาะสมหรือไม่ ) เราไม่สามารถรู้ได้ว่าเนื่องจากภาษาอังกฤษของเขาอาจมี จำกัด แม้ว่าคำตอบของ Tiesons ช่วยคุณได้ - ซึ่งดี - มันไม่เปลี่ยนความจริงที่ว่าคำตอบของ Ed สะท้อนให้เห็นถึงสิ่งที่ผู้ถามต้องการได้ดีที่สุด
Anders M.

2
@AndersM ฉันเข้าใจว่าถ้อยคำความคิดเห็นของฉันแย่มาก ... :-) ฉันควรจะเน้นจุดที่ไม่ใช่ผลลัพธ์ที่ฉันต้องการ
mxmissile

@AndersM ผู้ถามยอมรับคำตอบของ Tieson อย่างดีที่สุดดังนั้นฉันไม่แน่ใจว่าทำไมคุณถึงตัดสินใจเลือกเขา คำตอบ Tieson ให้ฉันช่วยฉันมากกว่าคำตอบของเอ็ด ดังนั้นไม่เพียงช่วยคนเดียว แต่ทุกคนที่มีปัญหาคล้ายกัน ดังนั้นทำไมไม่เพียง แต่รักษาคำตอบของ Tieson เอาไว้?
Kevin Voorn

คำตอบ:


106

ตัวควบคุมเป็นเพียงคลาส - ใหม่ขึ้นมาและเรียกวิธีการดำเนินการเช่นเดียวกับที่คุณต้องการสมาชิกชั้นเรียนอื่น ๆ :

var result = new ControllerB().FileUploadMsgView("some string");


76
คุณจะไม่พลาด ControllerContext การร้องขอและเพื่อน ๆ หากคุณทำเช่นนี้?
ขนยาว

20
การสร้างอินสแตนซ์ของตัวควบคุมไม่ใช่ความคิดที่ดีเพราะวงจรชีวิตอาจถูกควบคุมโดยแอปพลิเคชันอื่น เช่นเมื่อใช้คอนเทนเนอร์ IoC ควรนำไปใช้กับการสะสมทั้งหมด ฯลฯ
Mo Valipour

48
หากใช้ IoC ของคุณคุณจะได้รับตัวควบคุมที่มีประชากรผ่านvar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile

3
@mxmissile มันคุ้มค่าที่จะเพิ่มเป็นคำตอบใหม่มากกว่าความคิดเห็นที่นี่
Tieson T.

2
@ilasno คุณคุ้นเคยกับคำว่า "การผกผันของการควบคุม" หรือไม่? ประเด็นที่เขาทำคือถ้าคุณมีส่วนประกอบในตัวควบคุมที่ต้องถูกฉีดเข้าไปใน Constructor คำตอบของฉันก็ไม่ได้ผลเว้นแต่คุณจะใช้ DependencyResolver เป็นตัวระบุตำแหน่งของบริการ
Tieson T.

202

ในฐานะที่เป็น @mxmissile กล่าวว่าในการแสดงความคิดเห็นที่จะเป็นคำตอบที่ได้รับการยอมรับ, คุณไม่ควรขึ้นใหม่ควบคุมเพราะมันจะหายไปพึ่งพาการตั้งค่าสำหรับ IoC HttpContextและจะไม่ได้มี

คุณควรได้รับอินสแตนซ์ของคอนโทรลเลอร์ของคุณดังนี้:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);

สิ่งที่ฉันกำลังมองหา โปรดทราบว่าผู้ที่ไม่ได้ใช้ IoC ยังคงไม่ได้รับการHttpContextฉีดยา
brichins

var controllerจะได้รับการกำหนดประเภทControllerBใช่
DLeh

1
สิ่งนี้ทำให้ฉันใกล้ชิด แต่ปัญหาหนึ่งที่เกิดขึ้นก็คือในกรณีของฉัน controller.MyAction () ทำให้มีการอ้างอิงถึง User.Identity ซึ่งดูเหมือนจะเป็น uininstantiated
Robert H. Bourdeau

1
@ilasno ฉันสนิมบน MVC วันนี้ แต่ผมคิดว่าผมหมายความว่าจะมีจริงมี IoC ตั้งค่าที่จะได้รับวัตถุควบคุมประชากรอย่างเต็มที่ (เช่นที่เกี่ยวข้องHttpContext) ฉันเชื่อว่าฉันใช้วิธีนี้โดยไม่ใช้ IoC เพื่อรับวัตถุคอนโทรลเลอร์ "ตื้น" (ต้องการเพียงการเข้าถึงฟังก์ชันการทำงานบางอย่าง) และเริ่มสับสนว่าทำไมส่วนต่างๆ "หายไป" [กัน: ฉันทำงานรอบ ๆ ในขณะที่ยังใช้วิธีนี้อยู่ แต่อาจจะต้องปรับการทำงานนั้นให้เป็นคลาสที่ใช้ร่วมกัน] สำหรับการตั้งค่าและตัวเลือก IoC ฉันต้องอ้างถึงบทความอื่น ๆ / คำถาม SO
brichins

3
บางคนดำเนินไปด้วยการแก้ไขที่ไม่มีจุดหมาย ... โปรดทราบว่ามีคนแก้ไขคำตอบที่เปลี่ยนตัวแปร "controller" เป็น "ctrlr" ... ดังนั้นจึงควรอ่าน "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ;" หากผู้ใช้รายนั้นแก้ไขได้อย่างถูกต้อง
JoeSharp

62

ตัวอย่างของคุณดูเหมือนรหัส psuedo คุณต้องส่งคืนผลลัพธ์ของRedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });

4
จะต้องมีการชี้ให้เห็นว่าหากการกระทำเป้าหมายยอมรับ POST เท่านั้นสิ่งนี้จะไม่ทำงาน
Marco Alves

13
ส่งคืน 302 ซึ่งทำให้เกิดการเข้าชมเซิร์ฟเวอร์อื่นซึ่งไม่ใช่สิ่งที่คำถามถาม
rboarman

16

@DLeh พูดว่าใช้แทน

var controller = DependencyResolver.Current.GetService<ControllerB>();

แต่การให้ตัวควบคุมบริบทของตัวควบคุมมีความสำคัญโดยเฉพาะอย่างยิ่งเมื่อคุณต้องการเข้าถึงUserวัตถุServerวัตถุหรือHttpContextตัวควบคุมภายใน 'ลูก'

ฉันได้เพิ่มรหัสบรรทัด:

controller.ControllerContext = new ControllerContext(Request.RequestContext, controller);

หรือมิฉะนั้นคุณอาจใช้ System.Web เพื่อกำหนดบริบทปัจจุบันด้วยเช่นกันเพื่อเข้าถึงServerหรือออบเจกต์ metioned ก่อน

NB: ฉันกำลังกำหนดเป้าหมายกรอบรุ่น 4.6 (Mvc5)


4
หากคุณพยายามเรียกการกระทำในคอนโทรลเลอร์ที่ใช้ View (.. ) หรือ PartialView (... ) คุณต้องเปลี่ยน routeData ด้วยตนเองเพื่อให้ ASP.NET รู้วิธีค้นหามุมมองของคุณ controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";สมมติว่าคุณพยายามส่งคืนผลลัพธ์จากการดำเนินการดัชนีใน HomeController
Steven

@Steven ผมต้องใช้ค่าเหล่านี้มากกว่าthis controllerในที่สุดผลลัพธ์กลับมาผ่านตัวควบคุมภายใน (นี่) ดังนั้นนั่นคือสิ่งที่ท้ายที่สุดพยายามค้นหามุมมอง
aaaantoine

ฉันยังเพิ่มว่าคุณสมบัติ Url ไม่ได้เริ่มต้นเมื่อ DependencyResolver.Current.GetService <ControllerB> () ดังนั้นคุณต้องคัดลอกจากคอนโทรลเลอร์ปัจจุบันด้วยตนเอง
Ralfeus

ในการดำเนินการกำหนดเป้าหมายคุณควรใช้return View("ViewName");เพียงแค่return View();
mNejkO

9

ให้ตัวแก้ไขดำเนินการโดยอัตโนมัติ

ภายในตัวควบคุม:

public class AController : ApiController
{
    private readonly BController _bController;

    public AController(
    BController bController)
    {
        _bController = bController;
    }

    public httpMethod{
    var result =  _bController.OtherMethodBController(parameters);
    ....
    }

}

2
imo คำตอบที่สะอาดที่สุด แต่คุณควรตั้งค่าบริบทตัวควบคุมเป็นตัวควบคุมใหม่
Mafii

8

หากทุกคนกำลังดูวิธีการทำเช่นนี้ใน. net core ฉันทำได้โดยการเพิ่มคอนโทรลเลอร์ในการเริ่มต้น

services.AddTransient<MyControllerIwantToInject>();

จากนั้นฉีดเข้าไปในคอนโทรลเลอร์อื่น

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

     public controllerBeingInjectedInto(MyControllerIwantToInject myControllerIwantToInject)
{
       _myControllerIwantToInject = myControllerIwantToInject;
      }

จากนั้นเพียงแค่เรียกว่าเป็นเช่นนั้น _myControllerIwantToInject.MyMethodINeed();


4

นี่คือสิ่งที่ฉันกำลังค้นหาหลังจากพบว่าRedirectToAction()จะไม่ผ่านวัตถุคลาสที่ซับซ้อน

เป็นตัวอย่างฉันต้องการเรียกIndexComparisonวิธีการในLifeCycleEffectsResultsตัวควบคุมและส่งผ่านมันเป็นวัตถุคลาสที่ซับซ้อนชื่อรุ่น

นี่คือรหัสที่ล้มเหลว:

return RedirectToAction("IndexComparison", "LifeCycleEffectsResults", model);

น่าสังเกตว่า Strings, จำนวนเต็ม, ฯลฯ รอดชีวิตจากการเดินทางไปยังวิธีการควบคุมนี้ แต่วัตถุรายการทั่วไปได้รับผลกระทบจากสิ่งที่เตือนให้รำลึกถึงการรั่วไหลของหน่วยความจำ C

ตามที่แนะนำข้างต้นนี่คือรหัสที่ฉันแทนที่ด้วย:

var controller = DependencyResolver.Current.GetService<LifeCycleEffectsResultsController>();

var result = controller.IndexComparison(model);
return result;

ทั้งหมดทำงานตามที่ตั้งใจไว้ในตอนนี้ ขอบคุณที่เป็นผู้นำ


3

คำตอบของ Dlehนั้นถูกต้องและอธิบายวิธีรับอินสแตนซ์ของคอนโทรลเลอร์อื่นโดยไม่ต้องพึ่งพาการตั้งค่า IoC

อย่างไรก็ตามตอนนี้เราจำเป็นต้องเรียกใช้เมธอดจากคอนโทรลเลอร์อื่นนี้
คำตอบแบบเต็มจะเป็น:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");

คุณจะเรียกการกระทำ "MethodNameFromControllerB_ToCall" ได้อย่างไรหากต้องการพารามิเตอร์ ตัวอย่างเช่น MethodNameFromControllerB_ToCall (int somenum, string บางครั้ง)?
Patee Gutee

3

ฉันรู้ว่ามันเก่า แต่คุณสามารถ:

  • สร้างเลเยอร์บริการ
  • ย้ายวิธีการที่นั่น
  • วิธีการโทรในตัวควบคุมทั้งสอง

2

หากปัญหาคือการโทร คุณสามารถเรียกมันได้โดยใช้วิธีนี้

yourController obj= new yourController();

obj.yourAction();

1
pfft! ถ้าคุณคาดหวังผลลัพธ์จากการกระทำแทนล่ะ var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.