【问题标题】:How to use ControllerContext of a controller from another controller in ASP.NET MVC and C#?如何在 ASP.NET MVC 和 C# 中使用来自另一个控制器的控制器的 ControllerContext?
【发布时间】:2022-10-25 16:31:49
【问题描述】:

我知道以前有人问过这个问题,并且我已经阅读了很多关于它的帖子,但我似乎找不到任何帮助。

我有大约 15 个控制器(除其他外)将不同的文档输出为 ASP.NET MVC 视图。我正在尝试创建一种方法,通过生成单个 PDF 然后合并它们(合并例程不包含在下面的代码中),使用 SyncFusion 将各种这些不同的文档组合为 PDF。

这是我创建单个 PDF 文件的主要方法的一部分:

foreach (FormsSubmitted fs in formObjects)
{
    filename = "";

    switch (fs.FormName.ToUpper())
    {
        case "OT EVAL":
            OTEvalController cOTEVAL = new OTEvalController();
            filename = cOTEVAL.CreateOTEvalPrintPDF(fs.VisitId.ToString());
            cOTEVAL = null;
            break;

        case "OT POC":
            OTPOCController cOTPOC = new OTPOCController();
            filename = cOTPOC.CreatePrintOTPlanOfCarePDF(fs.VisitId.ToString());
            cOTPOC = null;
            break;

        case "OT PROGRESS NOTE":
            ProgressNoteController cOTPN = new ProgressNoteController();
            filename = cOTPN.CreateOTProgressNotePrintPDF(fs.VisitId.ToString());
            cOTPN = null;
            break;

        case "PT EVAL":
            PTEvalController cPTEVAL = new PTEvalController();
            filename = cPTEVAL.CreatePTEvalPrintPDF(fs.VisitId.ToString());
            cOTEVAL = null;
            break;

        case "PT POC":
            PTPOCController cPTPOC = new PTPOCController();
            filename = cPTPOC.CreatePTPOCPrintPDF(fs.VisitId.ToString());
            cPTPOC = null;
            break;

        case "PT PROGRESS NOTE":
            PTProgressNoteController cPTPN = new PTProgressNoteController();
            filename = cPTPN.CreatePrintPTProgressNotePDF(fs.VisitId.ToString());
            cPTPN = null;
            break;

        case "SP EVAL":
            SPEvalController cSPEVAL = new SPEvalController();
            filename = cSPEVAL.CreatePrintSPEvalPDF(fs.VisitId.ToString());
            cSPEVAL = null;
            break;

        case "SP POC":
            SPPOCController cSPPOC = new SPPOCController();
            filename = cSPPOC.CreatePrintSPPOCPDF(fs.VisitId.ToString());
            cSPPOC = null;
            break;

        case "SP PROGRESS NOTE":
            SPProgressNoteController cSPPN = new SPProgressNoteController();
            filename = cSPPN.CreatePrintSPProgressNotePDF(fs.VisitId.ToString());
            cSPPN = null;
            break;

        case "MSW EVAL":
            MSWEvalController cMSWEVAL = new MSWEvalController();
            filename = cMSWEVAL.CreateMSWEvalPrintPDF(fs.VisitId.ToString());
            cMSWEVAL = null;
            break;

        case "MSW POC":
            MSWPOCController cMSWPOC = new MSWPOCController();
            filename = cMSWPOC.CreateMSWPOCPrintPDF(fs.VisitId.ToString());
            cMSWPOC = null;
            break;

        case "COMMUNICATION/COORDINATION OF CARE":
            CommController cCOMM = new CommController();
            filename = cCOMM.CreatePrintCommunicationFormPDF(fs.VisitId.ToString());
            cCOMM = null;
            break;
    }

    if (filename != "")
        filenames.Add(filename);
}

这是创建单个 PDF 的方法之一的示例:

public string CreatePrintPTProgressNotePDF(string visitid)
{
    string filename = string.Format("PTProgressNote{0}.pdf", visitid);
    string DestPath = Path.Combine(HttpRuntime.AppDomainAppPath, "UploadedDocuments", "InvoiceForms", filename);

    if (!System.IO.File.Exists(DestPath))
    {
        // Getting Index view page as HTML
        ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, "PrintPTProgressNotePDF", "");

        PrintPTProgressNoteViewModel ptModel = PrintPTProgressNotePDF(visitid);

        string html = Utility.GetHtmlFromView(ControllerContext, viewResult, "PrintPTProgressNotePDF", ptModel, HttpContext.Request.Url.Scheme, HttpContext.Request.Url.Authority);
        string baseUrl = string.Empty;

        // Convert the HTML string to PDF using WebKit
        Syncfusion.HtmlConverter.HtmlToPdfConverter htmlConverter = new Syncfusion.HtmlConverter.HtmlToPdfConverter(Syncfusion.HtmlConverter.HtmlRenderingEngine.WebKit);

        Syncfusion.HtmlConverter.WebKitConverterSettings settings = new Syncfusion.HtmlConverter.WebKitConverterSettings();

        // Assign WebKit settings to HTML converter
        htmlConverter.ConverterSettings = settings;

        // Convert HTML string to PDF
        Syncfusion.Pdf.PdfDocument document = htmlConverter.Convert(html, baseUrl);

        document.Save(DestPath);
        document.Close(true);
    }

    return DestPath;
}

这是上面使用的GetHtmlFromView 方法,它也使用ControllerContext

public static string GetHtmlFromView(ControllerContext context, ViewEngineResult viewResult, string viewName, object model,
                string Scheme, string Authority)
{
    context.Controller.ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        // view not found, throw an exception with searched locations
        if (viewResult.View == null)
        {
            var locations = new StringBuilder();
            locations.AppendLine();

            foreach (string location in viewResult.SearchedLocations)
            {
                locations.AppendLine(location);
            }

            throw new InvalidOperationException(
                string.Format(
                    "The view '{0}' or its master was not found, searched locations: {1}", viewName, locations));
        }

        ViewContext viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);
        viewResult.View.Render(viewContext, sw);

        string html = sw.GetStringBuilder().ToString();
        string baseUrl = string.Format("{0}://{1}", Scheme, Authority);
        html = Regex.Replace(html, "<head>", string.Format("<head><base href=\"{0}\" />", baseUrl), RegexOptions.IgnoreCase);
        return html;
    }
}

Create PDF 方法从它们的父控制器工作,但是当我从另一个控制器调用它们时,我得到 ControllerContext 为空。

有任何想法吗?

【问题讨论】:

    标签: c# asp.net-mvc controller


    【解决方案1】:

    help page for the ControllerBase.ControllerContext 声明:

    IControllerActivator 在激活控制器时激活此属性。如果用户代码直接实例化一个控制器,getter 返回一个空的 ControllerContext。

    我尝试了如果我可以解决IControllerActivator 并让那个激活控制器,但 IControllerActivator 还需要一个控制器上下文,所以会出现同样的问题......

    我假设您的代码的foreach (FormsSubmitted fs in formObjects) 部分在您提到的父控制器中?

    所以选项1:

    只需将父上下文传递给孩子:

    OTEvalController cOTEVAL = new OTEvalController() { ControllerContext = this.ControllerContext };
    

    选项 2:

    按照帮助建议使用 IControllerActivator(或 IControllerFactory),您可以这样做:

    var controllerFactory = this.HttpContext.RequestServices.GetRequiredService<IControllerFactory>();
    
    var newContext = new ControllerContext(this.ControllerContext)
    {
        ActionDescriptor =
        {
            ControllerName = nameof(OTEvalController),
            ControllerTypeInfo = new TypeDelegator(typeof(OTEvalController)),
            MethodInfo = typeof(OTEvalController).GetMethod("CreateOTEvalPrintPDF")
        }
    };
    
    var resultController = controllerFactory.CreateController(newContext);
    

    选项 3:

    使用手动创建的上下文手动创建控制器:

    var resultController = new OTEvalController(_mediator) { ControllerContext = newContext }; // newContext from Option 2
    

    此选项的缺点是,如果您的控制器采用任何构造函数参数,您还必须手动提供它们。使用选项 2,它可以解析控制器及其所有依赖项

    边注:

    无论如何,这种设置本身似乎很奇怪。我建议重构您的控制器/逻辑,使其不依赖于 ControllerContext。就像将逻辑从控制器移动到不依赖于控制器属性的单独“管理器”类。然后,您的控制器应该提取“管理器类”的数据,并将其传递给它们。

    尝试制作一个单元测试,您可以在其中调用整个流程而不依赖于控制器的东西

    【讨论】:

    • 谢谢您的答复!我会尽快尝试您的建议。关于你的旁注......我继承了这段代码,并且几乎被其中的大部分内容所困扰。
    【解决方案2】:

    您可以使用 DI,将控制器注入控制器并使用它们,而不是创建每个控制器的新实例并丢失 ControllerContext。就像你的控制器的生命周期是由应用程序以正确的方式管理的。

    如果你需要一个例子,你可以检查=> https://stackoverflow.com/a/35056047/8404545

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-18
      • 2010-10-27
      • 2013-08-11
      • 2019-05-13
      • 2016-10-04
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      相关资源
      最近更新 更多