Tuesday, July 12, 2011

Soap Message Logging Against the Discovery Service in Microsoft Dynamics CRM 2011

I wanted to be able to log messages sent to and received from the Microsoft Dynamics CRM 2011 discovery service the other day and had an issue.  There was a nifty SoapLoggerOrganizationService class that is included in the SoapLogger solution that comes with the SDK in this relative location (sdk\samplecode\cs\client\soaplogger).  This class just inherited and extended the OrganizationService class to allow for easy logging of SOAP messages.  I use this thing all the time for logging soap messages because if you have every tried to use Fiddler against the CRM 2011 services you realized really quickly that you weren't getting anywhere with that.

I went ahead and wrote a class for this that is very similar in form and function to the SoapLoggerOrganizationService and thought long and hard (less than 2 seconds) about a name for this thing and thought I would call the new class.....ready...... wait for it.....  the SoapLoggerDiscoveryService class.  I will show you how to use it below and include a sample using the RetrieveOrganizationsRequest.

You can download the class it here:


You use it in the same way as the SoapLoggerOrganizationService.  To add it to the SoapLogger solution and try it out, follow these steps.

1. Copy the unzipped file to the solution folder and "include in project" from within visual studio.

2. It's now ready to use but you need to make a couple quick changes.

- in SOAPLogger.cs you need to add a namespace:
using Microsoft.Xrm.Sdk.Discovery;

- in SOAPLogger.cs you need to add the instantiation code for your new class.

Replace this:
 // Connect to the Organization service. 
    // The using statement assures that the service proxy will be properly disposed.
    using (_serviceProxy = new OrganizationServiceProxy(serverConfig.OrganizationUri,
                                                        serverConfig.HomeRealmUri,
                                                        serverConfig.Credentials,
                                                        serverConfig.DeviceCredentials))
    {
     // This statement is required to enable early-bound type support.
     _serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());

     IOrganizationService service = (IOrganizationService)_serviceProxy;


     using (StreamWriter output = new StreamWriter("output.txt"))
     {

      SoapLoggerOrganizationService slos = new SoapLoggerOrganizationService(serverConfig.OrganizationUri, service, output);

      //Add the code you want to test here:
      // You must use the SoapLoggerOrganizationService 'slos' proxy rather than the IOrganizationService proxy you would normally use.



     }


    }

WITH THIS:

    DiscoveryServiceProxy _discProxy;
    // Connect to the Organization service. 
    // The using statement assures that the service proxy will be properly disposed.
       using (_discProxy = new DiscoveryServiceProxy(serverConfig.DiscoveryUri,
                                                        serverConfig.HomeRealmUri,
                                                        serverConfig.Credentials,
                                                        serverConfig.DeviceCredentials))
    {
     // This statement is required to enable early-bound type support.
        _discProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());

     IDiscoveryService service = (IDiscoveryService)_discProxy;


     using (StreamWriter output = new StreamWriter("output.txt"))
     {

         SoapLoggerDiscoveryService sldc = new SoapLoggerDiscoveryService(serverConfig.DiscoveryUri, service, output);

      //Add the code you want to test here:
      // You must use the SoapLoggerDiscoveryService 'sldc' proxy rather than the IDiscoveryService proxy you would normally use.
         RetrieveOrganizationsRequest req = new RetrieveOrganizationsRequest();
         RetrieveOrganizationsResponse resp = (RetrieveOrganizationsResponse)sldc.Execute(req);


     }


    }

You will notice I was even nice enough to include here an example of a RetrieveOrganizationsRequest here for you also.

Now if you open the Output.txt you should see something like this:


HTTP REQUEST
--------------------------------------------------
POST https://dev.crm.dynamics.com/XRMServices/2011/Discovery.svc/web
Content-Type: text/xml; charset=utf-8
SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IDiscoveryService/Execute

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <request i:type="a:RetrieveOrganizationsRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts/Discovery">
        <a:AccessType>Default</a:AccessType>
        <a:Release>Current</a:Release>
      </request>
    </Execute>
  </s:Body>
</s:Envelope>
--------------------------------------------------

HTTP RESPONSE
--------------------------------------------------
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <ExecuteResponse xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <ExecuteResult i:type="a:RetrieveOrganizationsResponse" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts/Discovery">
        <a:Details>
          <a:OrganizationDetail>
            <a:Endpoints xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
              <a:KeyValuePairOfEndpointTypestringztYlk6OT>
                <b:key>WebApplication</b:key>
                <b:value>https://sogetiusa.crm.dynamics.com/</b:value>
              </a:KeyValuePairOfEndpointTypestringztYlk6OT>
              <a:KeyValuePairOfEndpointTypestringztYlk6OT>
                <b:key>OrganizationService</b:key>
                <b:value>https://sogetiusa.api.crm.dynamics.com/XRMServices/2011/Organization.svc</b:value>
              </a:KeyValuePairOfEndpointTypestringztYlk6OT>
              <a:KeyValuePairOfEndpointTypestringztYlk6OT>
                <b:key>OrganizationDataService</b:key>
                <b:value>https://sogetiusa.api.crm.dynamics.com/XRMServices/2011/OrganizationData.svc</b:value>
              </a:KeyValuePairOfEndpointTypestringztYlk6OT>
            </a:Endpoints>
            <a:FriendlyName>Sogeti USA</a:FriendlyName>
            <a:OrganizationId>f3740acf-9f06-4645-9661-8e521b236451</a:OrganizationId>
            <a:OrganizationVersion>5.0.9688.1155</a:OrganizationVersion>
            <a:State>Enabled</a:State>
            <a:UniqueName>crmNAorgf3740</a:UniqueName>
            <a:UrlName>sogetiusa</a:UrlName>
          </a:OrganizationDetail>
        </a:Details>
      </ExecuteResult>
    </ExecuteResponse>
  </s:Body>
</s:Envelope>
--------------------------------------------------


- I hope this helps.

2 comments:

  1. POST https://dev.crm.dynamics.com/XRMServices/2011/Discovery.svc/web
    Content-Type: text/xml; charset=utf-8
    SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IDiscoveryService/Execute

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
    <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <request i:type="a:RetrieveOrganizationsRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts/Discovery">
    <a:AccessType>Default</a:AccessType>
    <a:Release>Current</a:Release>
    </request>
    </Execute>
    </s:Body>
    </s:Envelope>
    I tried this request to Retrieve Organizations and I'm getting access denied response. Please help

    ReplyDelete
  2. How did you try to execute the request? I have had no luck using client script against the discovery service in general. I think it's just not allowed. Asking questions in the forums haven't turned up anything either.

    ReplyDelete