Monday, May 18, 2009

MS CRM 4.0 Generate SSRS reports by webservice ( pdf , HTML , CSV , etc..)

Below is a Generic SSRS web service reporting method that generates a byte[] perfect for use in an annotation or email attachment. The example method above it shows how to call this method and how to create an annotation attachment.

You can combine this with my post on driving email templates and generating email attachments to create some very powerful solutions. If you need a primer on activity parties see my post on creating activity parties.

Generating SSRS reports by web service is a very valuable tool. I frequently use this to generate custom reports from custom web applications running in iframes such that the report is run with the permissions of the user running the report. To take advantage of this the reports must be run against MS CRM’s filtered views.

To Generate an SSRS report by web service do the following:

1. Add a report execution web reference to your solution. Make sure this link is accessible. The url should be something like the following:
http://<SRS_Server_Name>/ReportServer/ReportExecution2005.asmx

2. Build up the parameters that you need to generate the report.

3. Select the Report Name of your Report

4. Specify the Type of Report

5. Specify the Device information ( formatting )

The example method CallReport() below demonstrates these steps. It in turn calls GenerateSRSbytes which makes the actual web service call.

using System.Net;
using Microsoft.Win32;
using ReportExecutionService;


public class SRSReporting
{

    public void CallReport()
    {
        string reportServiceUrl = "http://<SRS_Server_Name>/ReportServer/ReportExecution2005.asmx";

        // Create the Report Service Url from the registry
        RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\MSCRM", false);

        if (key != null)
        {
            reportServiceUrl = (string)key.GetValue("SQLRSServerURL") + @"/ReportExecution2005.asmx";
        }

        // Report parameter Name and Value fields are strings.

        string reportParameter = "type";
        string reportValue = "annual";

        // Create as many parameters as you need.
        var parameters = new[]
         {
             new ParameterValue {Name = "entityid", Value = "0031a390-0d42-de11-9aa9-0003ff517b20"},
             new ParameterValue {Name = reportParameter, Value = reportValue}
         };

        // Pathname to the report from Reporting Services, where TestReport is the rdl uploaded using http://<SRS_Server_Name>/Reports
        // Make sure that you set the Data Source properly
        // Make sure to set security properly
        string reportName = "/AutomatedCRMReporting/TestReport";

        // Specify what type of report you want to create HTML,PDF, CVS, Excel, Word, XML, MHTML, Image
        string reportFormat = "PDF";

        // Specify the device information to control the output of your report.
        //For device information See http://msdn2.microsoft.com/en-us/library/ms155397.aspx
        string deviceInformation = "<DeviceInfo><DpiX>240</DpiX><DpiY>240</DpiY><StartPage>0</StartPage></DeviceInfo>";

        // Generate a report in a format for a CRM annotation or email attachment
        byte[] generatedReport = GenerateSRSbytes(reportName, parameters, reportFormat, deviceInformation, reportServiceUrl, null, null, null);

        // An example attachment created from an SRS report
        //var newAnnotation = new annotation
        //  {
        //      objectid = CrmTypes.CreateLookup(regardingEntity, regardingId),
        //      objecttypecode = CrmTypes.CreateEntityNameReference(EntityName.contact.ToString()),
        //      isdocument = CrmTypes.CreateCrmBoolean(true),
        //      ownerid = CrmTypes.CreateOwner(EntityName.systemuser.ToString(), ownerId),
        //      filename = reportFileName,
        //      documentbody = Convert.ToBase64String(generatedReport),
        //      filesize = CrmTypes.CreateCrmNumber(generatedReport.Length),
        //      subject = reportFileName
        //  };

        //service.Create(newAnnotation);
    }


    /// <summary>
    /// Generates an SSRS Report and returns a byte[] for a CRM attachment
    /// </summary>
    /// <param name="reportPath">Name of the report.</param>
    /// <param name="parameters">The parameters.</param>
    /// <param name="outputFormat">The output format.</param>
    /// <param name="deviceInformation">The device information. See http://msdn2.microsoft.com/en-us/library/ms155397.aspx</param>
    /// <param name="ReportServiceUrl"></param>
    /// <param name="userName">if null, DefaultNetworkCredentials are used</param>
    /// <param name="passWord">if null, DefaultNetworkCredentials are used</param>
    /// <param name="domainName"></param>
    /// <returns></returns>
    public static byte[] GenerateSRSbytes(string reportPath, ParameterValue[] parameters, string outputFormat, string deviceInformation, string ReportServiceUrl, string userName, string passWord, string domainName)
    {
        string encoding;
        string mimeType;
        string extension;
        string[] streamIDs;
        string SessionId;
        string historyID = null;
        Warning[] warnings;

        // By default the Report will run with the permissions of the AD authenticated User.
        var rs = new ReportExecutionService.ReportExecutionService
        {
            Credentials = System.Net.CredentialCache.DefaultNetworkCredentials,
            Url = ReportServiceUrl
        };


        // Impersonate credentials if they are specified. 
        if (userName != null && passWord != null)
        {
            if (domainName == null)
            {
                rs.Credentials = new NetworkCredential(userName, passWord);
            }
            else
            {
                rs.Credentials = new NetworkCredential(userName, passWord, domainName);
            }
        }

        // Set timeout in seconds of the report takes a long time.
        //rs.Timeout = 600000;

        ExecutionHeader execHeader = new ExecutionHeader();
        rs.ExecutionHeaderValue = execHeader;

        var execInfo = new ExecutionInfo();
        execInfo = rs.LoadReport(reportPath, historyID);

        rs.SetExecutionParameters(parameters, "en-us");

        SessionId = rs.ExecutionHeaderValue.ExecutionID;

        // Render Report
        return rs.Render(outputFormat, deviceInformation, out extension, out mimeType, out encoding, out warnings, out streamIDs);

    }
}

Happy Coding!

5 comments:

Anonymous said...

I can't find the 'ReportExecutionService' object in the file ReportExecution2005.asmx (so error on compiling 'var rs = new
ReportExecutionService.ReportExecutionService'..) Do you have any idea why ? thanks

Aamir Bashir said...

Mark,

Thanks for sharing it with us. It is great way to generate report.

Mark Kovalcson said...

Anonymous, You need to add a web reference to your project with the SSRS web service before this will work.

Shivam Dixit said...

Mark,

I would be a great help if you could provide some light about report scheduling and how this topic can be clubbed together.

I would like to give you some insight about my query.
I would like to make an report that would auto generate at a particular time and that code would send an email attaching these reports.(pdf,HTML,csv,etc.)


Thanks and Regards,
Shivam Dixit

Mark Kovalcson said...

I have other blog articles that talk about how to send an email with an attachment from CRM.

You could have a scheduled commandline application that generated reports and emails. I've done that for a number of customers.

You could also Schedule reports to be run through SSRS. http://msdn.microsoft.com/en-us/library/ms159767.aspx