how to pass a complext model to Rotativa?

3.8k views Asked by At

I am using MVC 4 to create an application. I am using Rotativa to create a pdf. I am calling ActionAsPdf with a complex model

my model is

public class BrandingDetails
    {
        public int PDFFileNo { get; set; }
        public string LogoImageUrl { get; set; }
        public string WebsiteUrl { get; set; }
        public string PhoneNumber_AU { get; set; }
        public string PhoneNumber_NZ { get; set; }
        public string FacebookImageUrl { get; set; }
        public string TwitterImageUrl { get; set; }
        public string PinterestImageUrl { get; set; }
        public string YoutubeImageUrl { get; set; }
        public string CruiseCode { get; set; }
        public string ShipName { get; set; }
        public string PortName { get; set; }
        public string SailDate { get; set; }
        public string CruiseNights { get; set; }
        public string Destinations { get; set; }
        public string QuoteRef { get; set; }
        public string CruiseName { get; set; }
        public string DescriptionCruise { get; set; }

        public IEnumerable<CruiseOptions> CruiseOptions { get; set; }

    }

I am calling the following

var viewModel = QuoteHelper.GetViewModel(item);

 var pdfResult = new ActionAsPdf("CruiseDetails", quoteDetails, cruiseOptions);


var binary = pdfResult.BuildPdf(this.ControllerContext);

my Action method it is calling is

 public ActionResult CruiseDetails(BrandingDetails quoteDetails, IEnumerable<CruiseOptions> cruiseOptions)
        {

            return View("CruiseDetails", quoteDetails);

        }

but for some reason when I pass it to the Action all the data is passed bar the CruiseOptions .. that is set to no elements... how is that possible?

2

There are 2 answers

1
Sean Thorburn On

Too late, but following Josh's answer I have found a solution using JSON. I hope that this helps someone.

public class SalesReportModel : BaseModel
{
    public List<Guid> UserGuids { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }       
}

public ActionResult GenerateSalesReport(string json)
{
    SalesReportModel model = new JavaScriptSerializer().Deserialize<SalesReportModel>(json);
    return View("SalesReport", model);
}

public ActionResult SalesReport(SalesReportModel model)
{
    var json = new JavaScriptSerializer().Serialize(model);
    return new Rotativa.ActionAsPdf("GenerateSalesReport", new { json = json });            
}
0
Josh On

This is way, way too late, but I don't see a lot of answers to this particular issue out there, and I've struggled with it a very long time.

Rotativa's ActionToPdf cannot port non-primitive types to the view. More pedantically, complex objects have no way of translating into a GET query string that the receiving controller will understand. This unfortunately means that all enumerable container classes are out.

The long and short of this is that this behavior occurs because of Rotativa's reliance on the Action() method of the UrlHelper class to port route values and allow wkhtmltopdf to generate the view. When you call new ActionToPdf(), Action() reflects upon the input routing object and iterates its properties, creating a query string consisting of key-value pairs with your model's property names and their associated values. The value is derived using the ToString() method.

In the case of more complex data types, doing this to the value will yield the class name. Rotativa will, using this data, create a query string and pass this to the wkhtmltopdf driver upon which it is built, which will subsequently reach out to your intended page using a number of query variables. If you step through the Rotativa code as it executes, you will see this URL being built, and in the Text Visualizer of the string it's building, will undoubtedly espy something like this:

http://localhost/Home/About?yourModelsList=System.Collections.Generics.List&someOtherParameter=...

When the receiving controller method gets this data, while it attempts to bind all these query variables to your model, it will discard the enumerable field's value from the query string, resulting in your having nothing in the list.

I don't know of a great workaround to this at all. To date I've gone unanimously application-specific routes, which has required some...code quality sacrifices...I've been loathe to commit to, but didn't see another way at the time.

I've envisioned storing the view data in a Session variable and retrieving it through the controller as wkhtmltopdf calls it, but this would require some rather unsavory practices (sharing a session ID on the wire isn't good security practice at all). You might also entertain serializing the object, storing it in a database backend, and retrieving it somehow while the PDF-rendering controller executes. That seems like a lot of work for so simple a method as the Rotativa API presents...but if you're desperate enough...