How to Run the CRM Report through SDK

Question Status

Suggested Answer
Jayakumar asked a question on 12 Aug 2017 8:58 AM

Hi,

I want to connect to CRM Organization/API through SDK using my user id and run the SSRS report and download as PDF file. Is there any way to do this?

Thanks.

Reply
Suggested Answer
Stephen.King responded on 12 Aug 2017 2:52 PM

We do this exact thing with a custom workflow plugin Code below:

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;
using Microsoft.Crm.Sdk.Messages;
using System.Web;

namespace Plugins
{
    public class AttachAndSendReportServerPDF : CodeActivity
    {
        [RequiredArgument]
        [Input("Source Email")]
        [ReferenceTarget("email")]
        public InArgument<EntityReference> SourceEmail { get; set; }

        [RequiredArgument]
        [Input("Send Email When Complete?")]
        [Default("True")]
        public InArgument<bool> SendOnComplete { get; set; }

        [RequiredArgument]
        [Input("Report Server")]
        [Default("https://URL.ServerLocation.Com")]
        public InArgument<string> ReportServer { get; set; }

        [RequiredArgument]
        [Input("Report Directory")]
        [Default("")]
        public InArgument<string> ReportDirectory { get; set; }

        [Input("Parameter Name")]
        [Default("Id of Entity")]
        public InArgument<string> ParameterName { get; set; }
        
        [Input("Parameter is Entity Reference? (Current Record Guid)")]
        [Default("True")]
        public InArgument<bool> ParameterType { get; set; }

        [Input("Parameter Value (String - If Above Is False)")]
        [Default("")]
        public InArgument<string> ParameterStringValue { get; set; }
        
        protected override void Execute(CodeActivityContext _context)
        {
            string RSLink = "";
            try
            {
                IOrganizationServiceFactory factory = _context.GetExtension<IOrganizationServiceFactory>();
                IWorkflowContext _wfContext = _context.GetExtension<IWorkflowContext>();
                IOrganizationService _service = factory.CreateOrganizationService(_wfContext.UserId);

                RSLink = GenerateRSLink(_context, _wfContext);
                string Base64 = GetLinkContents(RSLink);
                CreateEmailAttachment(Base64, _service, _context);
                if (_context.GetValue(SendOnComplete))
                    SendEmail(_service, _context);
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException(ex.Message + " : " + RSLink);
            }
        }

        protected void SendEmail(IOrganizationService _service, CodeActivityContext _context)
        {
            EntityReference Email = _context.GetValue(SourceEmail);

            SendEmailRequest req = new SendEmailRequest();
            req.EmailId = Email.Id;
            req.TrackingToken = "";
            req.IssueSend = true;
            SendEmailResponse res = (SendEmailResponse)_service.Execute(req);
        }

        protected void CreateEmailAttachment(string Base64, IOrganizationService _service, CodeActivityContext _context)
        {
            Entity pdf = new Entity("activitymimeattachment");
            pdf["subject"] = "SSRS - Report";
            pdf["filename"] = "ssrsReport.pdf";
            pdf["body"] = Base64;
            pdf["mimetype"] = "application/pdf";
            pdf["objectid"] = _context.GetValue(SourceEmail);
            pdf["objecttypecode"] = "email";

            _service.Create(pdf);
        }

        protected string GetLinkContents(string URL)
        {
            WebClient client = new WebClient();
            client.UseDefaultCredentials = true;
			//You may or maynot need to do this. Depends on your requirements
            //client.Credentials = new NetworkCredential([Username], [Password], [Domain]);

            byte[] bytes = client.DownloadData(URL);
            client.Dispose();

            return Convert.ToBase64String(bytes);
        }

        protected string GenerateRSLink(CodeActivityContext _context, IWorkflowContext _wfContext)
        {
            string FullReportPath = string.Format("{0}/ReportServer?{1}&rs:Command=Render&rs:Format=PDF", _context.GetValue(ReportServer), Uri.EscapeUriString(_context.GetValue(ReportDirectory)));
            string Param_Name = _context.GetValue(ParameterName);
            if (Param_Name != null && Param_Name.Length > 0)
            {
                if (_context.GetValue(ParameterType))
                {
                    Guid Param_Id = _wfContext.PrimaryEntityId;
                    FullReportPath += string.Format("&{0}={1}", Param_Name, Param_Id.ToString());
                }
                else
                {
                    string Param_String = _context.GetValue(ParameterStringValue);
                    FullReportPath += string.Format("&{0}={1}", Param_Name, Param_String);
                }
            }
            return FullReportPath;
        }
    }
}
Reply
Suggested Answer
Henry Jammes responded on 13 Aug 2017 11:13 AM

To the best of my knowledge, you can't do this on a CRM Online scenario with a plugin (because of their isolation mode and SSRS Online Servers limitations).

Does your custom workflow work online @Stephen.King?

You can however achieve this with a JavaScript using unsupported code (see example here: www.c-sharpcorner.com/.../generate-pdf-file-using-javascript-in-microsoft-dynamics-crm-2015)

Reply
Aric Levin responded on 13 Aug 2017 11:32 AM

Is this for a separate console/windows/web application using SDK, and not from CRM environment directly?

Reply
Stephen.King responded on 13 Aug 2017 2:14 PM

It works on prem on 2016 and 365. Unsure if it works online haven't had chance to try it out.

Reply
Stephen.King responded on 13 Aug 2017 2:16 PM

This is a workflow plug-in, it can be used on prem. In the couple of use cases I've used it. We have a process which does a bunch of data updates then sends an email with the data PDF attached when done.

Reply
Suggested Answer
Jayakumar responded on 1 Nov 2017 3:50 AM

Thanks Stephen. I cannot use this in my case. But I learnt something new.

Reply
Suggested Answer
Stephen.King responded on 12 Aug 2017 2:52 PM

We do this exact thing with a custom workflow plugin Code below:

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;
using Microsoft.Crm.Sdk.Messages;
using System.Web;

namespace Plugins
{
    public class AttachAndSendReportServerPDF : CodeActivity
    {
        [RequiredArgument]
        [Input("Source Email")]
        [ReferenceTarget("email")]
        public InArgument<EntityReference> SourceEmail { get; set; }

        [RequiredArgument]
        [Input("Send Email When Complete?")]
        [Default("True")]
        public InArgument<bool> SendOnComplete { get; set; }

        [RequiredArgument]
        [Input("Report Server")]
        [Default("https://URL.ServerLocation.Com")]
        public InArgument<string> ReportServer { get; set; }

        [RequiredArgument]
        [Input("Report Directory")]
        [Default("")]
        public InArgument<string> ReportDirectory { get; set; }

        [Input("Parameter Name")]
        [Default("Id of Entity")]
        public InArgument<string> ParameterName { get; set; }
        
        [Input("Parameter is Entity Reference? (Current Record Guid)")]
        [Default("True")]
        public InArgument<bool> ParameterType { get; set; }

        [Input("Parameter Value (String - If Above Is False)")]
        [Default("")]
        public InArgument<string> ParameterStringValue { get; set; }
        
        protected override void Execute(CodeActivityContext _context)
        {
            string RSLink = "";
            try
            {
                IOrganizationServiceFactory factory = _context.GetExtension<IOrganizationServiceFactory>();
                IWorkflowContext _wfContext = _context.GetExtension<IWorkflowContext>();
                IOrganizationService _service = factory.CreateOrganizationService(_wfContext.UserId);

                RSLink = GenerateRSLink(_context, _wfContext);
                string Base64 = GetLinkContents(RSLink);
                CreateEmailAttachment(Base64, _service, _context);
                if (_context.GetValue(SendOnComplete))
                    SendEmail(_service, _context);
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException(ex.Message + " : " + RSLink);
            }
        }

        protected void SendEmail(IOrganizationService _service, CodeActivityContext _context)
        {
            EntityReference Email = _context.GetValue(SourceEmail);

            SendEmailRequest req = new SendEmailRequest();
            req.EmailId = Email.Id;
            req.TrackingToken = "";
            req.IssueSend = true;
            SendEmailResponse res = (SendEmailResponse)_service.Execute(req);
        }

        protected void CreateEmailAttachment(string Base64, IOrganizationService _service, CodeActivityContext _context)
        {
            Entity pdf = new Entity("activitymimeattachment");
            pdf["subject"] = "SSRS - Report";
            pdf["filename"] = "ssrsReport.pdf";
            pdf["body"] = Base64;
            pdf["mimetype"] = "application/pdf";
            pdf["objectid"] = _context.GetValue(SourceEmail);
            pdf["objecttypecode"] = "email";

            _service.Create(pdf);
        }

        protected string GetLinkContents(string URL)
        {
            WebClient client = new WebClient();
            client.UseDefaultCredentials = true;
			//You may or maynot need to do this. Depends on your requirements
            //client.Credentials = new NetworkCredential([Username], [Password], [Domain]);

            byte[] bytes = client.DownloadData(URL);
            client.Dispose();

            return Convert.ToBase64String(bytes);
        }

        protected string GenerateRSLink(CodeActivityContext _context, IWorkflowContext _wfContext)
        {
            string FullReportPath = string.Format("{0}/ReportServer?{1}&rs:Command=Render&rs:Format=PDF", _context.GetValue(ReportServer), Uri.EscapeUriString(_context.GetValue(ReportDirectory)));
            string Param_Name = _context.GetValue(ParameterName);
            if (Param_Name != null && Param_Name.Length > 0)
            {
                if (_context.GetValue(ParameterType))
                {
                    Guid Param_Id = _wfContext.PrimaryEntityId;
                    FullReportPath += string.Format("&{0}={1}", Param_Name, Param_Id.ToString());
                }
                else
                {
                    string Param_String = _context.GetValue(ParameterStringValue);
                    FullReportPath += string.Format("&{0}={1}", Param_Name, Param_String);
                }
            }
            return FullReportPath;
        }
    }
}
Reply
Suggested Answer
Henry Jammes responded on 13 Aug 2017 11:13 AM

To the best of my knowledge, you can't do this on a CRM Online scenario with a plugin (because of their isolation mode and SSRS Online Servers limitations).

Does your custom workflow work online @Stephen.King?

You can however achieve this with a JavaScript using unsupported code (see example here: www.c-sharpcorner.com/.../generate-pdf-file-using-javascript-in-microsoft-dynamics-crm-2015)

Reply
Suggested Answer
Jayakumar responded on 1 Nov 2017 3:50 AM

Thanks Stephen. I cannot use this in my case. But I learnt something new.

Reply