Tuesday, July 5, 2011

Using JScript to Access SOAP Web Services Synchronously in Microsoft Dynamics CRM 2011

Ok, I know I have posted a ton of examples of how to work with JScript web services asynchronously using callback mechanisms and such to make thing happen in the UI after the fact.  I just feel that this is the best way to go for MOST operations as it doesn't slow down the UI as much, but I think it is time to let people in on to how to do these sorts of things synchronously in Microsoft Dynamics CRM 2011.

I will use the example of a RetrieveMultiple request for this purpose that retrieves all accounts of a specific name and I can tell you that because the name is specific that this example will only return one result in my system. After I present the code I will break down the differences for you between asynchronous jscript and synchronous jscript calls.

Here is the Code:

if (typeof (SDK) == "undefined")
   { SDK = { __namespace: true }; }
       //This will establish a more unique namespace for functions in this library. This will reduce the 
       // potential for functions to be overwritten due to a duplicate name when the library is loaded.
       SDK.SAMPLES = {
           _getServerUrl: function () {
               ///<summary>
               /// Returns the URL for the SOAP endpoint using the context information available in the form
               /// or HTML Web resource.
               ///</summary>
               var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
               var serverUrl = "";
               if (typeof GetGlobalContext == "function") {
                   var context = GetGlobalContext();
                   serverUrl = context.getServerUrl();
               }
               else {
                   if (typeof Xrm.Page.context == "object") {
                         serverUrl = Xrm.Page.context.getServerUrl();
                   }
                   else
                   { throw new Error("Unable to access the server URL"); }
                   }
                  if (serverUrl.match(/\/$/)) {
                       serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                   } 
                   return serverUrl + OrgServicePath;
               }, 
           RetrieveMultipleRequest: function () {
               var requestMain = ""
               requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
               requestMain += "  <s:Body>";
               requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
               requestMain += "      <request i:type=\"a:RetrieveMultipleRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
               requestMain += "        <a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>Query</b:key>";
               requestMain += "            <b:value i:type=\"a:QueryExpression\">";
               requestMain += "              <a:ColumnSet>";
               requestMain += "                <a:AllColumns>true</a:AllColumns>";
               requestMain += "                <a:Columns xmlns:c=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
               requestMain += "              </a:ColumnSet>";
               requestMain += "              <a:Criteria>";
               requestMain += "                <a:Conditions>";
               requestMain += "                  <a:ConditionExpression>";
               requestMain += "                    <a:AttributeName>name</a:AttributeName>";
               requestMain += "                    <a:Operator>Equal</a:Operator>";
               requestMain += "                    <a:Values xmlns:c=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">";
               requestMain += "                      <c:anyType i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">TestAccountName</c:anyType>";
               requestMain += "                    </a:Values>";
               requestMain += "                  </a:ConditionExpression>";
               requestMain += "                </a:Conditions>";
               requestMain += "                <a:FilterOperator>And</a:FilterOperator>";
               requestMain += "                <a:Filters />";
               requestMain += "              </a:Criteria>";
               requestMain += "              <a:Distinct>false</a:Distinct>";
               requestMain += "              <a:EntityName>account</a:EntityName>";
               requestMain += "              <a:LinkEntities />";
               requestMain += "              <a:Orders />";
               requestMain += "              <a:PageInfo>";
               requestMain += "                <a:Count>0</a:Count>";
               requestMain += "                <a:PageNumber>0</a:PageNumber>";
               requestMain += "                <a:PagingCookie i:nil=\"true\" />";
               requestMain += "                <a:ReturnTotalRecordCount>false</a:ReturnTotalRecordCount>";
               requestMain += "              </a:PageInfo>";
               requestMain += "              <a:NoLock>false</a:NoLock>";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>RetrieveMultiple</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), false)
               // Responses will return XML. It isn't possible to return JSON.
               req.setRequestHeader("Accept", "application/xml, text/xml, */*");
               req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
               req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");             
               req.send(requestMain);
               
               //work with response here
               var strResponse = req.responseXML.xml;
               alert(strResponse.toString());
           },
 __namespace: true
};


Using this syntax you would reference the SDK.SAMPLES.RetrieveMultipleRequest namespace in the

You will see that I am alerting the response here, but you will probably want to parse out and work with the response easily using a technique like I illustrate herehttp://mileyja.blogspot.com/2011/03/microsoft-dynamics-crm-2011-parsing.html

Now for the differences in calling web services synchronously:

The biggest difference from a coding standpoint is the req.open method that uses a false parameter for an synchronous call versus a true for an asynchronous call. - shown below


               req.open("POST", SDK.SAMPLES._getServerUrl(), false)



Good and Bad for Asychronous:
- UI isn't hindered by call happening in the background, much better experience from UI standpoint.
- It is difficult to perform manipulations in UI based on the response, especially in a timely manner.

Good and Bad for Synchronous:
 - UI performance hit because UI will stop responding until response is received from call.  This impact could be negligible or catastrophic depending on the performance and latency of the server at any given time.  It also will be impacted based on the type of call.  For instance, a RetrieveAllEntitiesRequest, might take minutes to respond and lock up the UI for that entire period.  Sometimes this call might not return at all.
- It is easy to work with the response and work with the UI in the same thread and manipulate the UI based on the response.

1 comment:

  1. Hi,
    May i know how do we authenticate to MS CRM 2011 web services?



    I am using Lotus script to do the web services which is similar to VB script language.
    I am using the 2007 end point URL and now i want to switch to the 2011 web services (XRM Services).


    WebServiceURL = CRMURL+"/XRMServices/2011/Organization.svc/web"
    Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    Call objXMLHTTP.open ("POST", WebServiceURL , False,UserName,Password)
    Call objXMLHTTP.setRequestHeader("Content-Type", "text/xml; charset=utf-8" )
    Call objXMLHTTP.setRequestHeader("SOAPAction", SOAPAction)

    The above code is used to autenticate with the 2007 end point URL and is workimg fine, But it is not working for the 2011 URL.

    I am getting the Autentication failed Response as shown below when i tried the 'WhoAmI' request

    a:FailedAuthenticationAccess is denied.

    I guess the username and Password should set in someother way for crm 2011?
    Can you help me?

    Thanks
    Prasanth

    ReplyDelete