Tuesday, April 26, 2011

How to: Use Retrieve Messages in Jscript and .NET With the Microsoft Dynamics CRM 2011 SDK

Here is a quick demo on how to utilize the Retrieve message of the CRM 2011 Organization service in both C# and Jscript. I will provide two examples.  The first is a simple retrieve of a single entity by it's id.  The second retrieve request will navigate a relationship to retrieve not only the same contact entity, but also all the accounts for which that contact is associated.

CALL # 1



The first call will just be a simple retrieve that returns a contact entity based on it's ID GUID.
NOTE: In .NET you can then examine the Entity attribute of the response to get the main entity, and also iterate through the Entity.RelatedEntities attribute to access the related accounts.
First in C#:


      RetrieveRequest req = new RetrieveRequest();
      req.ColumnSet = new ColumnSet(true);
      req.Target = new EntityReference
      {
          Id = new Guid("007889EB-0B70-E011-8DD8-1CC1DEE8EA59"),
          LogicalName = "contact"

      };
      RetrieveResponse response = (RetrieveResponse)service.Execute(req);


Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

To understand how to parse the response please review my post on using the DOM parser.
http://mileyja.blogspot.com/2011/03/microsoft-dynamics-crm-2011-parsing.html

This example is asynchronous, if you want to learn how to make JScript SOAP calls synchronously please visit this posthttp://mileyja.blogspot.com/2011/07/using-jscript-to-access-soap-web.html



//main entry point
function runme() {
   SDK.SAMPLES.RetrieveContactRequest();
}

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;
               }, 
           RetrieveContactRequest: 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:RetrieveRequest\" 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>Target</b:key>";
               requestMain += "            <b:value i:type=\"a:EntityReference\">";
               requestMain += "              <a:Id>007889eb-0b70-e011-8dd8-1cc1dee8ea59</a:Id>";
               requestMain += "              <a:LogicalName>contact</a:LogicalName>";
               requestMain += "              <a:Name i:nil=\"true\" />";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>ColumnSet</b:key>";
               requestMain += "            <b:value i:type=\"a:ColumnSet\">";
               requestMain += "              <a:AllColumns>true</a:AllColumns>";
               requestMain += "              <a:Columns xmlns:c=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>Retrieve</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), true)
               // 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");
               var successCallback = null;
               var errorCallback = null;
               req.onreadystatechange = function () { SDK.SAMPLES.RetrieveContactResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       RetrieveContactResponse: function (req, successCallback, errorCallback) {
               ///<summary>
               /// Recieves the assign response
               ///</summary>
               ///<param name="req" Type="XMLHttpRequest">
               /// The XMLHttpRequest response
               ///</param>
               ///<param name="successCallback" Type="Function">
               /// The function to perform when an successfult response is returned.
               /// For this message no data is returned so a success callback is not really necessary.
               ///</param>
               ///<param name="errorCallback" Type="Function">
               /// The function to perform when an error is returned.
               /// This function accepts a JScript error returned by the _getError function
               ///</param>
               if (req.readyState == 4) {
               if (req.status == 200) {
               if (successCallback != null)
               { successCallback(); }
               }
               else {
                   errorCallback(SDK.SAMPLES._getError(req.responseXML));
               }
           }
       },
       _getError: function (faultXml) {
           ///<summary>
           /// Parses the WCF fault returned in the event of an error.
           ///</summary>
           ///<param name="faultXml" Type="XML">
           /// The responseXML property of the XMLHttpRequest response.
           ///</param>
           var errorMessage = "Unknown Error (Unable to parse the fault)";
           if (typeof faultXml == "object") {
               try {
                   var bodyNode = faultXml.firstChild.firstChild;
                   //Retrieve the fault node
                   for (var i = 0; i < bodyNode.childNodes.length; i++) {
                       var node = bodyNode.childNodes[i];
                       //NOTE: This comparison does not handle the case where the XML namespace changes
                       if ("s:Fault" == node.nodeName) {
                       for (var j = 0; j < node.childNodes.length; j++) {
                           var faultStringNode = node.childNodes[j];
                           if ("faultstring" == faultStringNode.nodeName) {
                               errorMessage = faultStringNode.text;
                               break;
                           }
                       }
                       break;
                   }
               }
           }
           catch (e) { };
        }
        return new Error(errorMessage);
     },
 __namespace: true
};


CALL # 2


The sample call I will use will retrieve a contact entity and all related account entities based on the "contact_customer_accounts" relationship.

NOTE: In .NET you can then examine the Entity attribute of the response to get the main entity, and also iterate through the Entity.RelatedEntities attribute to access the related accounts.
First in C#:

RetrieveRequest req = new RetrieveRequest();

      QueryExpression query = new QueryExpression();
      query.EntityName = "account";
      query.ColumnSet = new ColumnSet(true);
      Relationship relationship = new Relationship();

      query.Criteria = new FilterExpression();
      // name of relationship between team & systemuser
      relationship.SchemaName = "contact_customer_accounts";
      RelationshipQueryCollection relatedEntity = new RelationshipQueryCollection();
      relatedEntity.Add(relationship, query);
      RetrieveRequest request = new RetrieveRequest();
      request.RelatedEntitiesQuery = relatedEntity;
      request.ColumnSet = new ColumnSet(true);
      request.Target = new EntityReference
      {
          Id = new Guid("007889EB-0B70-E011-8DD8-1CC1DEE8EA59"),
          LogicalName = "contact"

      };
      RetrieveResponse response = (RetrieveResponse)service.Execute(request);

Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

To understand how to parse the response please review my post on using the DOM parser.
http://mileyja.blogspot.com/2011/03/microsoft-dynamics-crm-2011-parsing.html



//main entry point
function runme() {
   SDK.SAMPLES.RetrieveRelatedContactsRequest();
}

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;
               }, 
           RetrieveRelatedContactsRequest: 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:RetrieveRequest\" 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>Target</b:key>";
               requestMain += "            <b:value i:type=\"a:EntityReference\">";
               requestMain += "              <a:Id>007889eb-0b70-e011-8dd8-1cc1dee8ea59</a:Id>";
               requestMain += "              <a:LogicalName>contact</a:LogicalName>";
               requestMain += "              <a:Name i:nil=\"true\" />";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>ColumnSet</b:key>";
               requestMain += "            <b:value i:type=\"a:ColumnSet\">";
               requestMain += "              <a:AllColumns>true</a:AllColumns>";
               requestMain += "              <a:Columns xmlns:c=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>RelatedEntitiesQuery</b:key>";
               requestMain += "            <b:value i:type=\"a:RelationshipQueryCollection\">";
               requestMain += "              <a:KeyValuePairOfRelationshipQueryBaseX_PsK4FkN>";
               requestMain += "                <b:key>";
               requestMain += "                  <a:PrimaryEntityRole i:nil=\"true\" />";
               requestMain += "                  <a:SchemaName>contact_customer_accounts</a:SchemaName>";
               requestMain += "                </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: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:KeyValuePairOfRelationshipQueryBaseX_PsK4FkN>";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>Retrieve</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), true)
               // 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");
               var successCallback = null;
               var errorCallback = null;
               req.onreadystatechange = function () { SDK.SAMPLES.RetrieveRelatedContactsResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       RetrieveRelatedContactsResponse: function (req, successCallback, errorCallback) {
               ///<summary>
               /// Recieves the assign response
               ///</summary>
               ///<param name="req" Type="XMLHttpRequest">
               /// The XMLHttpRequest response
               ///</param>
               ///<param name="successCallback" Type="Function">
               /// The function to perform when an successfult response is returned.
               /// For this message no data is returned so a success callback is not really necessary.
               ///</param>
               ///<param name="errorCallback" Type="Function">
               /// The function to perform when an error is returned.
               /// This function accepts a JScript error returned by the _getError function
               ///</param>
               if (req.readyState == 4) {
               if (req.status == 200) {
               //***********************
               //   ALERT RESULT HERE
               //***********************
                    alert(req.responseXML.xml.toString());
               }
               else {
                   errorCallback(SDK.SAMPLES._getError(req.responseXML));
               }
           }
       },
       _getError: function (faultXml) {
           ///<summary>
           /// Parses the WCF fault returned in the event of an error.
           ///</summary>
           ///<param name="faultXml" Type="XML">
           /// The responseXML property of the XMLHttpRequest response.
           ///</param>
           var errorMessage = "Unknown Error (Unable to parse the fault)";
           if (typeof faultXml == "object") {
               try {
                   var bodyNode = faultXml.firstChild.firstChild;
                   //Retrieve the fault node
                   for (var i = 0; i < bodyNode.childNodes.length; i++) {
                       var node = bodyNode.childNodes[i];
                       //NOTE: This comparison does not handle the case where the XML namespace changes
                       if ("s:Fault" == node.nodeName) {
                       for (var j = 0; j < node.childNodes.length; j++) {
                           var faultStringNode = node.childNodes[j];
                           if ("faultstring" == faultStringNode.nodeName) {
                               errorMessage = faultStringNode.text;
                               break;
                           }
                       }
                       break;
                   }
               }
           }
           catch (e) { };
        }
        return new Error(errorMessage);
     },
 __namespace: true
};


Now you can call the runme function from your form jscript handler.
Thats all there is to it!

I hope this helps!

No comments:

Post a Comment