MVC ControllerContext,Threads / Tasks,Rotativa

时间:2015-09-30 21:37:54

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

Rotativa 1.6.4在Rotativa.dll中继续发出System.NullReferenceException

具体地说,它发生在BuildPdf(上下文)

我尝试过Thread,Task和其他背景类型的工作。为什么?使用ActionAsPdf(请参阅Here),PDF生成速度非常慢,因此我希望将其转换为新的线程/任务或后台作业,并且只记录错误,这样我就可以重新启动用户他们的方式尽快。

BuildPdf获取当前的控制器上下文,它始终为null我尝试使用httpcontext创建一个新的上下文,使得一个新的控制器实例都具有相同的结果。一旦它进入Rotativa.dll,无论我是在线程/任务之前是否传入它,或者在我调用BuildPdf()之前创建一个新上下文,上下文都会被强制删除;

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

此时我已经准备好在真正的PDF解决方案上花钱了,但到目前为止它们似乎都是PITA,并且即使你有最简单的儿童PDF需求,它们也只是平庸。 / p>

4 个答案:

答案 0 :(得分:0)

您是否尝试创建SynchronizationContext

看起来这个库是非线程安全的。

答案 1 :(得分:0)

我正在处理同样的问题。我试图模拟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)

答案 2 :(得分:0)

对于任何人都拉着他们的头发试图像我一样想出这个......我尝试了所有的东西,从使用DriverOptions(描述为here)传递cookie到渲染{{1}由Rotativa生成一个字符串(自定义Razor扩展)并将其转换为字节数组 - 但只是从ViewAsPdf更改为ActionAsPdf为我解决了这个问题。

无论出于何种原因PartialViewAsPdf包含与PartialViewAsPdf相同(或更好/改进的??)BuildPdf(ControllerContext)方法,但ActionAsPdf不包含ViewAsPdfPartialViewAsPdf中传递了ControllerContext类,并按照我的预期访问了特定于上下文的变量。

答案 3 :(得分:0)

试试这个!!

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