【问题标题】:MVC ControllerContext, Threads/Tasks, RotativaMVC ControllerContext、线程/任务、Rotativa
【发布时间】:2015-12-28 19:50:22
【问题描述】:

Rotativa 1.6.4 不断在 Rotativa.dll 中发出 System.NullReferenceException

具体发生在 BuildPdf(context)

我尝试过线程、任务和其他后台类型的工作。为什么? PDF 生成有时非常慢,并且使用 ActionAsPdf(请参阅Here)时出现错误,因此我想将其转为新的线程/任务或后台作业,并且只记录错误,这样我就可以让用户重新开始他们的工作尽快。

BuildPdf 采用当前控制器上下文,该上下文始终为 null 我尝试使用 httpcontext 创建一个新上下文,创建一个新的控制器实例,所有结果都相同。一旦它进入 Rotativa.dll,上下文就完全消失了,无论我是从我成为线程/任务之前传入它,还是在我调用 BuildPdf() 之前创建一个新的;

如何在开始线程/任务作业之前或在线程/任务作业中进入 Rotativa 之前获取有效的控制器上下文?

在这一点上,我已经准备好在真正的 PDF 解决方案上花费真金白银,但到目前为止,它们似乎都是 PITA,即使您对 PDF 有最简单的儿童需求,它们也最多只能是平庸的。

【问题讨论】:

    标签: c# asp.net-mvc multithreading pdf


    【解决方案1】:

    您是否尝试改为创建SynchronizationContext

    好像这个库是非线程安全的。

    【讨论】:

    • 据作者所说,库本身是线程安全的,但我发现它可能不是。我将如何使 SynchronizationContext 工作?我依稀熟悉知道它的存在。
    • 好吧,这方面的文档确实很少。由于您只能从主线程获取非空 SynchronizationContext.Current(),因此您应该声明一个委托函数,该函数将充当SynchronizationContext.Current() getter 的代理。然后在主线程中创建一个SynchronizationContext,并实现delegate返回Current()getter的值。最后,从工作线程调用委托以获取可以传递给库的上下文。
    【解决方案2】:

    我正在处理同样的问题。我尝试模拟 ControllerContext,使用 MvcContrib 创建 FakeControllerContexts(可以在 Rotativa 单元测试库中看到)等等……但是所有方法都给我带来了混乱、循环引用,并且它们在线程中运行不佳。

    为了从模型中调用函数,我最终使用了 RazorEngine(让模型数据填充 html)+ iTextSharp(将 html 转换为 pdf)而不是 Rotativa。这在我的项目(电子邮件、pdf、...)中创造了优雅的模板可能性。

    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using iTextSharp.text.html.simpleparser;
    
    ...
    public class PDFHelper { 
            public static byte[] GetPDFGromHTMLString(string pHTML) {
                byte[] bPDF = null;
    
                MemoryStream ms = new MemoryStream();
                TextReader txtReader = new StringReader(pHTML);
    
                // 1: create object of a itextsharp document class
                Document doc = new Document(PageSize.A4, 25, 25, 25, 25);
    
                // 2: we create a itextsharp pdfwriter that listens to the document and directs a XML-stream to a file
                PdfWriter oPdfWriter = PdfWriter.GetInstance(doc, ms);
    
                // 3: we create a worker parse the document
                HTMLWorker htmlWorker = new HTMLWorker(doc);
    
                // 4: we open document and start the worker on the document
                doc.Open();
                htmlWorker.StartDocument();
    
                // 5: parse the html into the document
                htmlWorker.Parse(txtReader);
    
                // 6: close the document and the worker
                htmlWorker.EndDocument();
                htmlWorker.Close();
                doc.Close();
    
                bPDF = ms.ToArray();
    
                return bPDF;
            }
    

    在模型中我将其用作:

    using RazorEngine;
    using RazorEngine.Templating;
    ...
    string razorText = System.IO.File.ReadAllText(HostingEnvironment.MapPath(@"~/Views/MyReport.cshtml"));
    string body = Razor.Parse(razorText, model);
    byte[] pdfBinary = MyFramework.PDFHelper.GetPDFGromHTMLString(body);
    /**other stuff */
    

    如果您不想在 RazorEngine 中处理 RequestContext,则不应使用 Html 和 Url 助手(将 @Html.DisplayFor(model => ..) 更改为 @Model.MyProperty)

    【讨论】:

      【解决方案3】:

      试试这个!!

      Task.Factory.StartNew(() =>                  
       {
           Do Something here
       },
       CancellationToken.None,
       TaskCreationOptions.PreferFairness,
       TaskScheduler.FromCurrentSynchronizationContext() 
      );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-21
        • 1970-01-01
        相关资源
        最近更新 更多