Thursday, July 7, 2011

Convert FetchXML to QueryExpression Using .NET or JScript With the Microsoft Dynamics CRM 2011 SDK

This illustration shows how to convert a FetchXML query to a QueryExpression in Microsoft Dynamics CRM 2011 in code in jscript and also C#  using the FetchXmlToQueryExpressionRequest.   This example will be given in SOAP (JScript) and in C# (.NET).

Ok, here is what the code look like!
First in C#:

FetchXmlToQueryExpressionRequest req = new FetchXmlToQueryExpressionRequest();
req.FetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
              "<entity name='account'>" +
                  "<attribute name='name' /> " +
                  "<attribute name='address1_city' /> " +
                  "<attribute name='primarycontactid' /> " +
                  "<attribute name='telephone1' /> " +
                  "<attribute name='accountid' /> " +
                  "<order attribute='name' descending='false' /> " +
                  "<filter type='and'>" +
                      "<condition attribute='statecode' operator='eq' value='0' /> " +
                  "</filter>" +
                  "<link-entity name='contact' from='contactid' to='primarycontactid' visible='false' link-type='outer' alias='accountprimarycontactidcontactcontactid'>" +
                      "<attribute name='emailaddress1' /> " +
                  "</link-entity>" +
              "</entity>" +
          "</fetch>";

FetchXmlToQueryExpressionResponse resp = (FetchXmlToQueryExpressionResponse)service.Execute(req);

//How to access the query expression from the response
//resp.Query

If you need help instantiating a service object in .NET within a plugin check out this post:
http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

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

Now in Jscript:


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

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;
               }, 
           FetchXmlToQueryExpressionRequest: 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=\"b:FetchXmlToQueryExpressionRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
               requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <c:key>FetchXml</c:key>";
               requestMain += "            <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">&lt;fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'&gt;&lt;entity name='account'&gt;&lt;attribute name='name' /&gt; &lt;attribute name='address1_city' /&gt; &lt;attribute name='primarycontactid' /&gt; &lt;attribute name='telephone1' /&gt; &lt;attribute name='accountid' /&gt; &lt;order attribute='name' descending='false' /&gt; &lt;filter type='and'&gt;&lt;condition attribute='statecode' operator='eq' value='0' /&gt; &lt;/filter&gt;&lt;link-entity name='contact' from='contactid' to='primarycontactid' visible='false' link-type='outer' alias='accountprimarycontactidcontactcontactid'&gt;&lt;attribute name='emailaddress1' /&gt; &lt;/link-entity&gt;&lt;/entity&gt;&lt;/fetch&gt;</c:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>FetchXmlToQueryExpression</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.FetchXmlToQueryExpressionResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       FetchXmlToQueryExpressionResponse: 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
};




To understand how to parse the response please review my post on using the DOM parser.
Now you can call the SDK.SAMPLES.FetchXmlToQueryExpressionRequest function from your form jscript handler.
Thats all there is to it!

I hope this helps!

2 comments:

  1. What do I do with the response?

    Your example says:
    /// For this message no data is returned so a success callback is not really necessary.

    But doesn't it return the view???

    ReplyDelete
  2. That message I think is generated by my tool. To get the response in code you will want to hook up a success call back or review the link above about making calls synchronously. You would use the queryexpression as the basis for another call that requires a queryexpression and cannot use fetchxml, like the retrieve KB article requests and things. This particular message in the SDK isn't as helpful as the opposite call which also exists where you can convert your queryexpression to fetchxml because fetchxml can also be used for reporting in BIDS and other things.

    ReplyDelete