November 6, 2013

Simulating a custom web service in Microsoft Dynamics CRM Online

 

The problem

Stating the obvious - it’s becoming more and more popular nowadays to move functionality from the server to the client side. Especially in web applications where rich client applications with asynchronous data retrieval and processing allow a much richer user experience.

This trend is also present in Dynamics CRM with the introduction of Web Resources, which in theory allow you to build any kind of functionality and embed it into CRM – as IFrames launched from the main menu, popups opened from ribbon buttons, parts of dashboards etc. The possibilities are unlimited.

Dynamics CRM exposes web services and methods which allow to launch basically any functionality exposed by the SDK from the client side (using different methods like REST or direct organization service requests). Although it gives a lot of possibilities it’s basically limited to atomic operations on a single record and fetch requests with a very limited query language (FetchXML or even more so OData).

In cases where the business logic is more complicated and requires fetching data from several places this approach will be extremely inefficient  and time consuming to develop (think live updating segmentation results on the client side for example). Another example where using the build in web services is not the best idea is when you need to display data aggregated from maybe thousands (millions) of records which cannot be achieved by single FetchXML queries. Downloading all that data to the client just to process it and display some result will be slow, error prone and time consuming to build.

In a broader view the “client” doesn’t necessarily have to be a web browser. It can as well be a windows application, external service etc.

Summing up, this is what we would like to avoid:

image

On-premise solution

In an on-premise environment the natural solution would be to create a custom web service (like WCF, ASP.NET MVC Web API etc.) and host it in IIS next to CRM. In that service we can write basically any code using the full server side SDK. What we get is something like this, which probably is the best solution:

image

What about online?

Unfortunately this cannot be done in CRM Online. Simply because we cannot deploy any services next to CRM. We could try on Azure in the same datacenter but that’s an additional cost and component to maintain. Also this will always be slower than hosting it directly on the same machine, network latency can be a serious issue when doing thousands of requests.

Are we stuck with doing multiple requests to get our data? No.

There are at least two possible solutions to the problem.

Solution 1 - Request handling entity

The first solution would be to create a dedicated entity for request handling. It could be generic with two text fields – request and response into which we put the request parameters and calculated response or domain specific with exact fields matching the request we are making.

We than create a record of that entity after which a pre-operation plugin fires, does all the calculations and writes them back to the response field(s). Afterwards we query the entity to retrieve the response and delete it. Graphically it would look like:

image

This method requires 3 web requests. Alternatively we could skip the 3rd one (cleanup) and do that with a workflow or just leave the request records and clean them up on schedule.

A big advantage of this method is that since we process all the data in a plugin we gain transactional consistency. If anything fails during the processing all the changes are rolled back.

This method can be used to do all types of operations in CRM – not only data retrieval, but also creating and updating existing data.

The downside is that we actually create a record in CRM that doesn’t have any business meaning. It does waste a little recourses and might be fragile to transactional locking.

Solution 2 – Use exceptions to your advantage (only good for querying data)

There is another solution that doesn’t require for any records to be created in CRM. When an InvalidPluginExecutionException is thrown inside a CRM plugin, we can provide it with a error message that is returned to the client. Why not use that error message to actually return the response? Smile

image

This way the “request helper entity” record is never created and we get the response directly. No need to re-query to retrieve the response and no need to manage the unnecessary helper records.

The only downside is we cannot do any processing of data in this approach, only query it. This is because when the exception is thrown we rollback all the changes we did in the system.

Example of how to read the response from Solution 2

The error text returned by CRM will always be in the form:

SOME_GENERIC_TEXT OUR_ERROR_MESSAGE SOME_GENERIC_TEXT

The simplest approach would be to clearly separate our response from the rest of the message using an unique separator like @##@ on both sides of the message. This way we get

SOME_GENERIC_TEXT @##@RESPONSE@##@ SOME_GENERIC_TEXT

Which is simple enough to parse in any client technology using string split.

When using a HTML client application this would look something like this

$.ajax({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    datatype: "json",
    url: Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/MyEntitySet",
    data: jsonRequestData,
    beforeSend: function (XMLHttpRequest) {
        XMLHttpRequest.setRequestHeader("Accept", "application/json");
    },
    success: function (data, textStatus, XmlHttpRequest) {
        // Should never get here
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) {       
        var responseText = XMLHttpRequest.responseText.split("@##@")[1];
    }
});

Summary

This article presented two methods of client side retrieving and processing data in CRM online which are a good alternative to building dedicated web services.

May 6, 2013

CRM 2011 ribbon button data dependency

 

The problem

It is not uncommon for a button in the ribbon to be enabled / disabled based on data from another (probably related) entity, or fields from the current entity which are not on the form.

In such cases it is necessary to query the organization service in order to retrieve the required data.

The most commonly used AJAX requests by default are asynchronous. This means that a callback function is called with the response as argument.
$.ajax({
  dataType: "json",
  url: url,
  data: data,
  success: success
});
image

This method is great in general, because it doesn’t block the UI thread in the browser. Although when used in functions determining if a ribbon button should be enable they won’t work. The obvious reason is that the callback is called after the base function has finished. So the base function that is supposed to return the yes/no answer does not have access to the results.
image

 

The solution

image

This synchronous behavior is trivial to achieve.

var jsonResponse= $.parseJSON($.ajax({
            type: "GET",
            dataType: "json",
           
url: Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc/ContactSet?QUERY
   
        cache: false,
            async: false
        }).responseText);

Setting the async parameter to false will cause the query to be synchronous. It will block the UI thread, but in most cases for such a short period of time it’s barely noticeable.

After retrieving the jsonResponse all you need to do is to parse it and return true / false from the base function.