CRM 2011 – Render SSRS Report from Plugin

Recently I had to play with SSRS web services. The objective was to render multiple reports in pdf format from within a CRM 2011 plugin, and to upload the files either in notes or in Sharepoint document location. After doing a lot of research, here is the function I came up with to call the SSRS web service. All of it does not completely make sense, and I had a hard time putting this all together. Documentation is not easy to find and understand.

 

private Byte[] RenderReport(string serviceURL, string reportPath, string reportName, ParameterValue[] parameters)
{
byte[] result;
string encoding;
string mimeType;
Warning[] warnings = null;
string[] streamids;
string extension;

BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
binding.Security.Transport.Realm = string.Empty;
binding.MaxReceivedMessageSize = 2147483647;
string deviceInfo = @”<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>”;
EndpointAddress endpoint = new EndpointAddress(serviceURL);

ReportExecutionServiceSoapClient client = new ReportExecutionServiceSoapClient(binding, endpoint);

client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;

ExecutionInfo executionInfo;
ExecutionHeader executionHeader;
ServerInfoHeader serverInfoHeader;

executionHeader = client.LoadReport(null, reportPath, null, out serverInfoHeader, out executionInfo);
executionHeader.ExecutionID = executionInfo.ExecutionID;

if (parameters != null)
{
client.SetExecutionParameters(executionHeader, null, parameters, “en-us”, out executionInfo);
}

ServerInfoHeader Info = client.Render(executionHeader, null, “PDF”, deviceInfo, out result, out extension, out mimeType, out encoding, out warnings, out streamids);

return result;
}

CRM Plugin reset form

Having recently been playing with multiple forms in CRM 2011, I wanted to make sure that when opening a record, the user would always be presented with the main information form. At first, I decided to udpate the UserUIEntitySettings from within the form onload. But I must say I had a few problems with this option:

  1. First, I decided to use a getFormId javascript function. It worked fine until I realized most users were using CRM with a language that was different from the base language. This made the form id’s unaccessible to them, and thus the function always returned nothing.
  2. Second, I decided to use the Xrm.Page.ui.formSelector object. But for some reason, while this worked well in the main form, it turned out that in the other form, the Xrm.Page.ui.formSelector.items collection was always null. I still did not figure out why.

So in the end I decided to go the plugin way, and implemented the below function in a PreRetrieve event. Notice that the mainFormId parameter is passed to the plugin using the secure/unsecure parameters (unsecure in this case), since it is not accessible for retrieve to most users because of their current language.

I also have to retrieve the concerned entity’s object type code. Indeed, you must be careful if you use early binding in your plugin NOT TO use new_entity.EntityTypeCode, as this will be different between environments.

private void ResetMainForm(context)

        {
            RetrieveEntityRequest entityRequest = new RetrieveEntityRequest
            {
                EntityFilters = EntityFilters.All,
                LogicalName = account.EntityLogicalName
            };
            RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)context.OrganizationService.Execute(entityRequest);
            QueryExpression query = new QueryExpression(UserEntityUISettings.EntityLogicalName);
            query.Criteria.AddCondition(“ownerid”, ConditionOperator.Equal, context.PluginExecutionContext.UserId.ToString());
            query.Criteria.AddCondition(“objecttypecode”, ConditionOperator.Equal, entityResponse.EntityMetadata.ObjectTypeCode.Value.ToString());
            query.ColumnSet = new ColumnSet(true);
            EntityCollection UISettingsCollection = context.OrganizationService.RetrieveMultiple(query);
            if (UISettingsCollection.Entities.Count > 0)
            {
                //update the last viewed formId:
                UserEntityUISettings settings = (UserEntityUISettings)UISettingsCollection[0];
                settings.LastViewedFormXml = “<MRUForm><Form Type=”Main” Id=”” + mainFormId + “” /></MRUForm>”;
                context.OrganizationService.Update(settings);
            }
        }

[ad]

CRM 2011 plugins, Filtered Views and impersonation

With Microsoft Dynamics CRM 2011, accessing directly the CRM database is supported only through the filtered views. This is how dynamic Excel files work by the way.

Recently I had to design a quite complex query involving about 12 inner joins. I wrote the query in SQL and then had to use it in inside a plugin. The problem is that when accessing the CRM database from a plugin, the connection is opened with the identity of the CRMAppPool. If it’s Network Service or another computer account, then normally no data should be accessible through filtered views. If it’s a domain account that also is a CRM system administrator, then potentially too much (ie all) data is available.

The solution I found to work around this issue is to add this command on top of the SQL query:

Execute As User=’domainusername’

The Username is of course retrieved inside the plugin from the context’s userid.

While this worked well, apparently (at least that’s what I understood) this induced another problem which is that the user account under which the query is being run doesn’t necesserily have the right to call the Execute as command, which could apparently happen. And so I started receiving this error:

A severe error occurred on the current command. The results, if any, should be discarded.

So, I added the “Revert” command at the end of the query, which seemed to solve this issue!

And now I have a sql query, using filtered views, that impersonates the plugin context’s userid!

Next time I’ll use Linq…

The Plugin Profiler

I recently discovered the Dynamics CRM plugin profiler.

The Plug-in Profiler is a tool that profiles the execution of plug-ins for an enhanced debugging experience in Microsoft Visual Studio 2010. This tool, which can be run from the Command Prompt window or from within the Plug-in Registration tool, makes developing plug-ins against Microsoft Dynamics CRM 2011 and Microsoft Dynamics CRM Online quicker and easier. In addition, users can profile the execution of failing plug-ins and send the results to the plug-in developer or ISV for analysis. The developer can replay the plug-in execution and debug the plug-in remotely even when disconnected from the Microsoft Dynamics CRM server. The Plug-in Profiler is available in the 5.0.5 release and newer releases of the Microsoft Dynamics CRM SDK

The tool can be used in either the debug or replay mode. Each of these modes is described in the following sections.

1. Debug a Plug-in using the Plug-in Profiler

To debug a plug-in

  1. Build the Plug-in Registration tool according to the instructions in its Readme file. The tool can be found in the ToolsPluginRegistration folder of the SDK download.
  2. Run the Plug-in Registration tool.
  3. Connect to a Microsoft Dynamics CRM server and organization. For more information on connecting to a server and organization, refer to the SDK topic: Walkthrough: Register a Plug-in Using the Plug-in Registration Tool.
  4. Register a plug-in and step on the Microsoft Dynamics CRM server. Keep a copy of the debug compiled plug-in assembly on the computer where you are running the tool.
  5. In the tool’s main window, select Install Profiler. You will now see a Plug-in Profiler node in the list.
  6. Select a plug-in step and click Profile to enable profiling.
  7. Perform the operation in Microsoft Dynamics CRM that causes the plug-in to run. For example, if the step is configured for an update to an account, then update an account.
  8. After the plug-in throws an exception and the Business Process Error dialog is displayed, click Download Log File and save this file. Alternately, if the plug-in does not throw an exception, click Stop Profiling.
  9. In the Plug-in Registration tool, click Debug.
  10. In the Debug Existing Plug-in dialog box, provide the location of the previously saved log file.
  11. In Step #2, specify the location of the plug-in assembly.
  12. Launch Visual Studio 2010 and attach the debugger to the PluginRegistration.exe process.
  13. Set a breakpoint in the plug-in code.
  14. Click Start Plug-in Execution in the Debug Existing Plug-in dialog box.
  15. After a slight delay, the plug-in will execute using the same execution context that was passed to it by the Microsoft Dynamics CRM server and the debugger will stop execution on the breakpoint that you previously set.
  16. Continue debugging the plug-in as you would normally do. Any traces that the plug-in outputs are shown in the Debug Existing Plug-in dialog box.

At this point you can alter the plug-in code, build it, re-attach the debugger to the PluginRegistration.exe process, and click Start Plug-in Execution to continue with your debugging session. While performing these operations, you do not need to close the Debug Existing Plug-in form.

You do not need to re-deploy the plug-in to the Microsoft Dynamics CRM server until after you have fixed the code problem. This debugging scenario works even if you have an optimized version of the plug-in on the server and a debug version of the plug-in on the computer where you are debugging.

2. Replay Plug-in Execution

Replaying plug-in execution does not require a connection to a Microsoft Dynamics CRM server and organization. The advantage of this method is that you can obtain the plug-in execution profile from a customer and debug the plug-in remotely. A restriction of the replay feature is that you cannot change the sequence of calls your plug-in code makes in the debugger while you are debugging.

The replay feature provides the plug-in with a snapshot of the call data and event execution context from the Microsoft Dynamics CRM server. You are getting the same events, GUIDs, and so on from calls to the Organization service but no data is being modified on the server as you debug the plug-in. During the debugging procedure in the previous section, the plug-in actually connects to the server and makes calls in real time.

To replay plug-in execution

  1. Launch Visual Studio 2010 and attach the debugger to the PluginRegistration.exe process.
  2. Click Replay Plug-in Execution in the Debug Existing Plug-in dialog box.
  3. Enter the log and plug-in assembly locations in the dialog box.
  4. Click Start Plug-in Execution.
  5. Debug the plug-in as you would normally.

 

3. Run the Plug-in Profiler Standalone

The profiler can be executed from a Prompt window independent of the Plug-in Registration tool. This is useful to obtain the plug-in profile log from a customer’s Microsoft Dynamics CRM server to debug a failed plug-in. A developer can then use that log to replay the plug-in’s execution in the Plug-in Registration tool and debug the plug-in using Visual Studio 2010.

To run the plug-in profiler from a command prompt

  1. Open a Command Prompt window and set the working directory to the Bin folder in the SDK download.
  2. Type the command: PluginProfiler.Debugger.exe /?.
  3. Review the supported parameter list and re-run the PluginProfiler.Debugger.exe program with the appropriate parameters.