Rotativa Value cannot be null. Parameter name: con

2019-08-26 20:46发布

问题:

I am trying to use Rotativa to Generate a PDF and return the bytes, however I am getting the error:

Value cannot be null. Parameter name: context

Here is my code:

public string getReportsPDF(string community, string procedure)
{
    SiteSuperReportsController controller = new SiteSuperReportsController();

    string value = "";
    byte[] pdfBytes = new byte[] { };

    if (procedure == "GetProductionTasks")
    {
        var actionPDF = new Rotativa.ActionAsPdf("RedBluePDF", new { community = community, procedure = procedure })
        {
            PageSize = Size.A4,
            PageOrientation = Rotativa.Options.Orientation.Landscape,
            PageMargins = { Left = 1, Right = 1 }
        };
        try
        {
            pdfBytes = actionPDF.BuildFile(controller.ControllerContext);
            value = "Works";
        }
        catch(Exception e)
        {
            value = e.Message.ToString();
        }
    }
    return value;
}

The value returned is Value cannot be null. Parameter name: context

What am I doing wrong?

回答1:

One option is to move the call to BuildFile into an action method of a controller, where the controller context is available as property called ControllerContext.

If you need to create your controller manually like in your example, you will have to create the context yourself. Derek Comartin shows in his blog post Using Razor in a Console Application (outside of ASP.NET Core MVC) how to do this for an ASP.Core 2 project. For your scenario, try changing the

pdfBytes = actionPDF.BuildFile(controller.ControllerContext);

to

pdfBytes = actionPDF.BuildFile(CreateDummyControllerContext("SiteSuperReports"));

using the following methods:

    private ControllerContext CreateDummyControllerContext(string controllerName)
    {
        var context = new ControllerContext
        {
            HttpContext = new DefaultHttpContext
            {
                RequestServices = GetServiceProvider()
            },
            RouteData = new RouteData
            {
                Values = {{"controller", controllerName}}
            },
            ActionDescriptor = new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor
            {
                RouteValues = new Dictionary<string, string>(),
            }
        };

        return context;
    }

    // see https://codeopinion.com/using-razor-in-a-console-application/
    private ServiceProvider GetServiceProvider()
    {
        var services = new ServiceCollection();
        services.AddSingleton(PlatformServices.Default.Application);
        var environment = new HostingEnvironment
        {
            ApplicationName = Assembly.GetEntryAssembly().GetName().Name
        };
        services.AddSingleton<IHostingEnvironment>(environment);
        services.Configure<RazorViewEngineOptions>(options =>
        {
            options.FileProviders.Clear();
            options.FileProviders.Add(new PhysicalFileProvider(Directory.GetCurrentDirectory()));
        });
        services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
        services.AddSingleton<DiagnosticSource>(new DiagnosticListener("Microsoft.AspNetCore"));

        services.AddLogging();
        services.AddMvc();

        return services.BuildServiceProvider();
    }

You might need to add the Microsoft.Extensions.PlatformAbstractions package.