Serenity: Send Email with rendered PDF Attachment

Created on 26 Oct 2017  路  10Comments  路  Source: serenity-is/Serenity

Hi everyone,

I've a grid with inline buttons: one of these opens a dialog where an email will be sent with a PDF attachment, which I'd like to render before the email sending (it's not on the file system).
I create a service that do that, but I get an error in the file ReportController.cs. It seems the URL is null, but I don't understand why.

Could anyone help me ?

Thank you.

Here is the line that generate the error:

rigaerrore

My grid:

grid

My service in XYZEndpoint.cs:

service

Most helpful comment

No, ControllerContext is a public property of ControllerBase that is a base class of Controller.
Then, in your action method in endpoint or controller, you have already ControllerContext. Use it.

byte[] bytes = new ReportController().RenderAsPdf(report, "myKey", "myOpt", ControllerContext);

All 10 comments

Hi @kjaraa.
If you call the action method ReportController.Render or ReportController.Download (which are the standard action that Serenity use) the Request (HttpRequestBase type) contains valid information.
But if you call RenderAsPdf directly, you must pass a ControllerContext that contains context information.

E.g. :

private byte[] RenderAsPdf(IReport report, string key, string opt, ControllerContext context = null)
{
    string externalUrl = string.Empty;
     if (context == null)
         externalUrl = Config.Get<EnvironmentSettings>().SiteExternalUrl ??
         Request.Url.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/");
     else
     {
         externalUrl = ((System.Web.HttpRequestWrapper)((System.Web.HttpContextWrapper)((System.Web.Mvc.Controller)context.Controller)
                    .Url.RequestContext.HttpContext).Request)
                    .Url.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/");
            }
    [...]

I have used this way to generate a PDF on the fly without output it.

Sei italiana? :)

Thanks @Estrusco. I'll try it and I let you know if I solved it. S矛, sono italiana :)

I modified the RenderAsPdf method. I'd like to ask you: when I call the method I should simply instance a new ControllerContext simply or I should do something else ?

Here is my method:

_// Creation of PDF Report_
Report PDF report = new ReportPDF { Codice = EntityId };
object data = report.GetData(); // My Context

_// Call the method to render PDF_
byte[] bytes = new ReportController().RenderAsPdf(report, "myKey", "myOpt", new ControllerContext())

Thanks you very much !! :)

No, ControllerContext is a public property of ControllerBase that is a base class of Controller.
Then, in your action method in endpoint or controller, you have already ControllerContext. Use it.

byte[] bytes = new ReportController().RenderAsPdf(report, "myKey", "myOpt", ControllerContext);

It works, thank you very much to devote your time for me !! :)

You're welcome :D

This is a great post which is helping me with solving a task. Instead of attaching a report to an email, I need to save it to a database. I am using the following code to accomplish this:

            protected override void AfterSave() <-- from my save handler
            {
                base.AfterSave();

                IDbConnection connection = SqlConnections.NewByKey("Default");

                using (var uow = new UnitOfWork(connection))
                {
                    string key = "Default.Permit";
                    string opt = "{\"RequestId\":" + Row.RequestId + "}";
                    var reportInfo = ReportRegistry.GetReport(key);

                    var report = (IReport) JsonConvert.DeserializeObject(opt.TrimToNull() ?? "{}",
                        reportInfo.Type, JsonSettings.Tolerant);

                    byte[] bytes = new ReportController().RenderAsPdf(report, key, opt,
                        ((PermitSaveRequest<MyRow>) this.Request).Ctxt);

                    SaveRequest<AttachmentRow> sra = new SaveRequest<AttachmentRow>();
                    AttachmentRow ar = new AttachmentRow
                    {
                        RequestId = Row.RequestId,
                        AttachmentBytes = new MemoryStream(bytes),
                        Type = "PERMIT",
                        Size = bytes.Length,
                        MimeType = "application/pdf",
                        FileName = Row.RequestId + "-PERMIT.pdf",
                        Description = "Permit"
                    };
                    sra.Entity = ar;
                    SaveResponse sva = new AttachmentRepository().Create(uow, sra);
                    uow.Commit();
                }
            }
......
        public object GetData() <--- from my IReport class
        {
            var data = new PermitReportData();

            if(this.RequestId <= 0) throw new ArgumentException("Missing request id");

            using (var connection = SqlConnections.NewFor<PermitRow>())
            {
                var o = PermitRow.Fields;

                data.Permit = connection.TryById<PermitRow>(this.RequestId, q => q
                     .SelectTableFields()
                     /*.Select(o.EmployeeFullName)
                     .Select(o.ShipViaCompanyName)*/) ?? new PermitRow();

                var od = PermitContractorRow.Fields;
                data.Contractors = connection.List<PermitContractorRow>(q => q
                    .SelectTableFields()
                    .Select(od.ContractorName)
                    .Select(od.ContractorLicense)
                    .Where(od.RequestId == this.RequestId));

                /*var c = CustomerRow.Fields;
                data.Customer = connection.TryFirst<CustomerRow>(c.CustomerID == data.Order.CustomerID)
                    ?? new CustomerRow();*/
            }

            return data;
        }

Unfortunately, this is what I get in the generated PDF:

image

Can anyone see what I would be doing wrong to get the database timeout? Many thanks in advance.

Seem there is a record blocked. Check if you have pending change in record or non closed connection.

In AfterSave, the main transaction should already be committed. Unless I am
missing something...

I will open a new issue on this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gfo2007 picture gfo2007  路  3Comments

ga5tan picture ga5tan  路  3Comments

Pinellus picture Pinellus  路  3Comments

Amitloh picture Amitloh  路  3Comments

dkontod picture dkontod  路  3Comments