How to Execute FetchXML in CRM 2011 using a CRM 20

2019-01-24 10:52发布

问题:

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.

回答1:

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

  • XrmSvcToolkit
  • XrmServiceToolkit
  • CrmFetchKit.js

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).



回答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.



回答3:

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));


回答4:

"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.