How to Execute FetchXML in CRM 2011 using a CRM 2011 webservice and JavaScript?
Asked Answered
A

4

8

I want to execute FetchXML queries in a CRM 2011 environment using the CRM 2011 SOAP web services and JavaScript.

I have found a number of articles like this one showing how to use the 4.0 web service that is still available in the 2011 environment, but I do not want to do this.

This link seems to indicate that IOrganizationService.RetrieveMultiple can handle FetchXML. However, I do not want to use managed code for this.

I've come across this link that shows essentially what I want to do in the RetrieveMultiple function, but I want to be able to pass in existing FetchXML that I've written, not a new filter expression.

Azygous answered 3/5, 2011 at 23:40 Comment(0)
J
7

To execute fetchxml queries with JavaScript a bunch of frameworks /libraries are available:

Instead of writing the code by hand these libraries provide a simple way to perform several operations and access the results. But take into account that not all libraries (currently) support cross-browser (Q2.2012).

Jemy answered 15/5, 2012 at 7:51 Comment(4)
Thanks Thuld. I don't think these were available at the time I posted my question, but it looks like there are some great options now.Azygous
Any comments regarding pros/cons of the 3? Am thinking I should start using one but wonder what I should be considering in order to decide...Infection
Ok I am the author of the the CrmFetchKit, so I prefer this one :) But, as always it depends. The XrmServiceToolkit supports more operations/messags an the other hand, the CrmFetchKit offers cross-browser support.Jemy
Good news: The CrmFetchKit supportes CRM 2013 without any changes, it simply works :)Jemy
A
2

The "Capture Sample HTTP Request and Response" section of this MSDN article outlines how to get a SOAP message that is sent to CRM 2011 from managed code.

The "Execute the Query" section of this MSDN article gives an example of using 2011's IOrganizationService.RetrieveMultiple in managed code to execute a FetchXML query.

Using both of these samples, you can extract a sample SOAP message for RetrieveMultiple that contains a FetchXML query.

The "Create a JScript Library" section of the first MSDN article shows how to perform an Execute request in JavaScript against the 2011 SOAP endpoint. Replace the Assign SOAP request in this example with the RetrieveMultiple SOAP message that you get from executing the managed code.

This enables you to execute a FetchXML request in JavaScript against the 2011 SOAP endpoint.

Here are some snippets from a JavaScript library I wrote using the information above:

(function (window, undefined) {
    var _window = undefined;

    if (window.Xrm)
        _window = window;
    else if (window.parent.Xrm)
        _window = window.parent;
    else throw new Error("Unable to determine proper window");

    (function (Crm) {
        (function (Service, $, JSON, Xrm) {
            if (!Xrm)
                throw new Error("Unable to locate Xrm");
            if (!JSON)
                throw new Error("Unable to locate JSON");
            if (!$)
                throw new Error("Unable to locate jQuery");

            Service.Create = function (ODataSetName, EntityObject) {
                if (!EntityObject) {
                    throw new Error("EntityObject is a required parameter");
                    return;
                }
                if (!ODataSetName) {
                    throw new Error("ODataSetName is a required parameter");
                    return;
                }
                var jsonEntityObject = JSON.stringify(EntityObject);

                var req = new XMLHttpRequest();
                req.open("POST", Service.GetODataEndPoint() + "/" + ODataSetName, false);
                req.setRequestHeader("Accept", "application/json");
                req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                req.onreadystatechange = function () {
                    debuggingCallBack(this);
                };

                req.send(jsonEntityObject);

            };
            function debuggingCallBack(req) {
                if (req.readyState == 4 /* complete */) {
                    if (req.status == 201 || req.status == 204 || req.status == 1223) {
                        //Success
                        //201 = create
                        //204 = update
                        //1223 = delete
                    }
                    else {
                        //Failure
                        debugger;
                    }
                }
            };


            Service.Fetch = function (FetchXML) {
                var request = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
                request += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
                request += "<s:Body>";
                request += "<RetrieveMultiple xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
                request += "<query i:type=\"a:FetchExpression\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
                request += "<a:Query>";
                request += Service.FetchEncode(FetchXML);
                request += "</a:Query>";
                request += "</query>";
                request += "</RetrieveMultiple>";
                request += "</s:Body>";
                request += "</s:Envelope>";

                var req = new XMLHttpRequest();
                req.open("POST", Service.GetSOAPEndPoint(), false)
                req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/RetrieveMultiple");
                req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
                req.setRequestHeader("Content-Length", request.length);
                req.send(request);

                results = Service.GetResults(req.responseXML);

                return results;
            };
            Service.Delete = function (ODataSetName, EntityID) {
                if (!EntityID) {
                    throw new Error("EntityID is a required parameter");
                    return;
                }
                if (!ODataSetName) {
                    throw new Error("ODataSetName is a required parameter");
                    return;
                }

                var req = new XMLHttpRequest();
                req.open("POST", Service.GetODataEndPoint() + "/" + ODataSetName + "(guid'" + EntityID + "')", false)
                req.setRequestHeader("Accept", "application/json");
                req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                req.setRequestHeader("X-HTTP-Method", "DELETE");
                req.onreadystatechange = function () {
                    debuggingCallBack(this);
                };
                req.send();

            };


            Service.GetServerUrl = function () {
                var serverUrl = null;
                serverUrl = Xrm.Page.context.getServerUrl();
                if (serverUrl.match(/\/$/)) {
                    serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                }
                return serverUrl;
            };
            Service.GetODataEndPoint = function () {
                return Service.GetServerUrl() + "/XRMServices/2011/OrganizationData.svc";
            };
            Service.GetSOAPEndPoint = function () {
                return Service.GetServerUrl() + "/XRMServices/2011/Organization.svc/web";
            };

            Service.GetResults = function (responseXML) {
                var sFetchResult = responseXML.selectSingleNode("//RetrieveMultipleResult").xml;

                var oResultDoc = new ActiveXObject("Microsoft.XMLDOM");
                oResultDoc.async = false;
                oResultDoc.loadXML(sFetchResult);

                var oResults = new Array(oResultDoc.firstChild.firstChild.childNodes.length);

                var iLen = oResultDoc.firstChild.firstChild.childNodes.length;
                for (var i = 0; i < iLen; i++) {

                    var oResultNode = oResultDoc.firstChild.firstChild.childNodes[i];
                    var oBE = new BusinessEntity(oResultNode.selectSingleNode("//a:LogicalName").text);

                    var iLenInner = oResultNode.firstChild.childNodes.length;
                    for (var j = 0; j < iLenInner; j++) {
                        var oRA = new Object();

                        var value = null;
                        if (oResultNode.firstChild.childNodes[j].lastChild.childNodes.length == 3) {
                            if (oResultNode.firstChild.childNodes[j].lastChild.getElementsByTagName("a:Id").length == 1)
                                value = oResultNode.firstChild.childNodes[j].lastChild.getElementsByTagName("a:Id")[0].text;
                            if (oResultNode.firstChild.childNodes[j].lastChild.getElementsByTagName("a:Value").length == 1)
                                value = oResultNode.firstChild.childNodes[j].lastChild.getElementsByTagName("a:Value")[0].text;
                        }
                        if (!value)
                            value = oResultNode.firstChild.childNodes[j].lastChild.text;

                        oRA["value"] = value;

                        oBE.attributes[oResultNode.firstChild.childNodes[j].firstChild.firstChild.text] = oRA;
                    }

                    oResults[i] = oBE;
                }
                return oResults;
            };

            Service.BusinessEntity = function BusinessEntity(sName) {
                this.name = sName;
                this.attributes = new Object();
            };

            Service.FetchEncode = function (FetchXML) {
                var c;
                var HtmlEncode = '';

                if (FetchXML == null) {
                    return null;
                }
                if (FetchXML == '') {
                    return '';
                }

                for (var cnt = 0; cnt < FetchXML.length; cnt++) {
                    c = FetchXML.charCodeAt(cnt);

                    if (((c > 96) && (c < 123)) ||
                            ((c > 64) && (c < 91)) ||
                            (c == 32) ||
                            ((c > 47) && (c < 58)) ||
                            (c == 46) ||
                            (c == 44) ||
                            (c == 45) ||
                            (c == 95)) {
                        HtmlEncode = HtmlEncode + String.fromCharCode(c);
                    }
                    else {
                        HtmlEncode = HtmlEncode + '&#' + c + ';';
                    }
                }

                return HtmlEncode;
            };
        } (Crm.Service = Crm.Service || {}, _window.jQuery, _window.JSON, _window.Xrm));
    } (_window.Crm = _window.Crm || {}));
} (window));
Azygous answered 6/5, 2011 at 17:52 Comment(4)
While this method does enable you to execute a FetchXML request in JavaScript against the 2011 SOAP endpoint, it doesn't return XML in the same format as the 4.0 web service did. I was hoping I could just update the FetchXML calls for a CRM 4.0 to 2011 migration project, but it isn't going to be that easy.Azygous
In the Service.Fetch function, where is "results" defined before it is returned?Peptonize
good catch. i'll take a look at the source and get back with you on that.Azygous
Sorry it took so long to update this. I've updated the code to include the entire javascript file. This should be much easier to resue. The GetResults function isn't foolproof, so if anyone has suggestions on how to improve it, I'd be happy to hear those.Azygous
S
2

From the link you posted to the Microsoft SDK, you will see how to connect to the ODATA service. As you may have already found, ODATA doesn't allow you to execute fetch.

Instead, you will need to use the SOAP service (/XrmServices/2011/Organization.svc), and pass your fetch using Retrieve Multiple.

Here's a more detailed look at using the 2011 service through JavaScript: http://blog.customereffective.com/blog/2011/05/execute-fetch-from-javascript-in-crm-2011.html

Here is another blog post which parses the returned XML and builds an easily consumable JavaScript object: http://blog.customereffective.com/blog/2011/05/parsing-and-consuming-the-crm-2011-soap-service-inside-javascript.html

The 2011 Organization Service is quite different in it's return, so it won't be plug-n-play from your 4.0 stuff; however, the 2011 endpoint has a lot of nice improvements.

Sheelah answered 7/5, 2011 at 5:5 Comment(2)
The migration that I'm working with uses the Ascentium CRM Helper for the 4.0 web service. I created a similar helper for the 2011 web services that returns the same business object as the Ascentium helper did. I think there's room for improvement on the conversion to the buisness object, but so far it seems to work for my current project. I am interested in reading your upcomming post. Maybe we could compare our approaches?Azygous
Wow, I like the Ascentium services and oddly enough there are some similar elements, so hopefully the changes will be minimal. This is only Fetch right now. By the way, if you aren't using online, then you can comment out some of their CreateXMLHTTP code and connect locally without having to publish (which is pretty convenient). You should be able to copy the CreateXMLHTTP code and the 3 helper classes and plug them into the stuff I posted.Sheelah
I
1

"REST is an architectural style in which every resource is addressed by using a unique URI." http://msdn.microsoft.com/en-us/library/gg334279.aspx

You will not be able to use the REST end-point if you need to use FetchXml.

The alternative is to construct a SOAP message as you saw in your CRM4 examples. I haven't done this myself yet, but perhaps you could use a tool like Fiddler to see what the SOAP message look like so you can replicate them in your environment which your modified FetchXml.

Anyway, just an idea for now. Let me know how it goes, and if you manage to resolve it perhaps post your solution.

Idellaidelle answered 4/5, 2011 at 0:50 Comment(1)
Thanks for the clarification on the REST endpoint. I'll be sure to post back once I figure this one out.Azygous

© 2022 - 2024 — McMap. All rights reserved.