Send Data from CRM Online to Azure bus - part 2

As seen on the article part 1, we are now able to send data from the CRM to Azure bus pretty easily in fact !
The only remaining "problem" is that you don't have raw XML by default. We will see what are your options to clarify this point.

We have two possible scenarios :
1. Use a "Queue" contract on the Plugin Registration Tool
2. Use an intermediate "Queue" on Service Bus to process the binary XML

1.Usage of "Queue" Contract on the PRT

I've seen the article : https://community.dynamics.com/crm/f/117/t/155628 which simply refer to the fact that the XML is either in binary mode or raw mode depending on the Contract we define in the Plugin Registration Tool.

So far it looks really cool, but i don't have this "Queue" contract in my list when I setup the Service Endpoint to Service Bus.

So at least for my case, it's not possible, that's why i went for the scenario 2.

2. Use an intermediate "Queue" to decrypt the content.

So here we want to be able to access readable data to use them much easier for whatever your plan is :

  • Send data to an other system
  • Send an email using some Logic Apps
  • Etc...
a. Decrypt the content

Just as reminded we were getting something like this but just receiving the message from the CRM :

@RemoteExecutionContext/http://schemas.microsoft.com/xrm/2011/Contracts    i)http://www.w3.org/2001/XMLSchema-instance@BusinessUnitId�$58bcb6af-7879-e511-80e7-3863bb347a80@
CorrelationId�$2352076a-49b8-4f24-aa79-b09ff2647049@Depth�@InitiatingUserId�$40f20074-ede3-48cc-aafc-750a1275b99b@InputParameters    aBhttp://schemas.datacontract.org/2004/07/System.Collections.Generic@KeyValuePairOfstringanyType^key�Target^value.type�Entity@  
Attributes@KeyValuePairOfstringanyType^key�  
be_goapproved^value.type�    b:boolean   b http://www.w3.org/2001/XMLSchema�@KeyValuePairOfstringanyType^key�be_authorizedengl^value.type�    b:boolean   b http://www.w3.org/2001/XMLSchema�@KeyValuePairOfstringanyType^key�modifiedonbehalfby^value.nil�@KeyValuePairOfstringanyType^key�be_leadidentified^value.type�   b:boolean   b http://www.w3.org/2001/XMLSchema�@KeyValuePairOfstringanyType^key�estimatedclosedate^value.type�  
b:dateTime    b http://www.w3.org/2001/XMLSchema�  ًc�@KeyValuePairOfstringanyType^key�  
modifiedon^value.type�  
b:dateTime    b http://www.w3.org/2001/XMLSchema��CB �T�H@KeyValuePairOfstringanyType^key�be_grossrevenueweighted^value.type�Money@Value�       �      @KeyValuePairOfstringanyType^key�timezoneruleversionnumber^value.type�b:int b http://www.w3.org/2001/XMLSchema�@KeyValuePairOfstringanyType^key�be_grossrevenue^value.type�Money@Value�        �      @KeyValuePairOfstringanyType^key�name^value.type�b:string   b http://www.w3.org/2001/XMLSchema�CRM to Azure Service Bus  

In this example, i will show you the method i used to transform this into a nice JSON object. I went for JSON as it's easy to use today with the famous library "Newtonsoft.Json".

So here is the first snippet to go from the unreadable data to a wellknown Entity object :

var stream = message.GetBody<Stream>();  
DataContractSerializer serializer = new DataContractSerializer(typeof(RemoteExecutionContext));  
XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max);  
var deserializedBody = serializer.ReadObject(reader);  
IPluginExecutionContext plugin = (IPluginExecutionContext)deserializedBody;

Entity entity = (Entity)plugin.InputParameters["Target"];  

As you can see here, instead of using a StreamReader, I prefer to go with a "DataContractSerializer" plus "XmlDictionaryReader" which allow me to then get the "IPluginExecutionContext" we use everyday in our Plugins.

At this point, with those 6 lines of code, we have an Entity object which is much more easier to manipulate to do data check, error handling, or what you want to accomplish.

b. Converting the Entity object into a JSON object.

I said earlier that in my particular case, I decided to go for a JSON object before sending it to my other systems.

More or less at the same period of time, I heard of the ExpandoObject which is really interesting (check Here for more details).

Here are the different steps :

dynamic entityToSend = new ExpandoObject();  
var ets = entityToSend as IDictionary<String, object>;  

I prepare my expandoObject which is a dynamic variable of course, plus a variable which will contain the Pair : Attribute technical name / Value (object as i need to be able to use the same way to push String, Decimal, Guid, ... in my object).

Next step is to loop on all attributes I have in the entity I just received :

for (var attribute in entity.Attributes)  
{ 
     ets[attribute.Key] = attribute.Value;
}

Here it's a simple example, which won't handle the EntityReference, Money, OptionsetValue type of object. Based on what you want, you need to treat them specially to retrieve only the Name or Guid or Value.

Now we have our ExpandoObject filled with the data coming from the Entity object.
It's time to use it to send a new message to my next queue inside Azure Service Bus again in our scenario.

Which gives the following piece of code :

var dynamicjson = JsonConvert.SerializeObject(entity);

using (Stream stream = new MemoryStream())  
{
    using (TextWriter writer = new StreamWriter(stream))
    {
        writer.Write(dynamicjson);
        writer.Flush();
        stream.Position = 0;

        var message = new BrokeredMessage(stream);

        message.Properties.Add("messageid", messageId);
        message.Properties.Add("fromsystem", "crm");
        message.ContentType = "text/plain";

        try
        {
            client.Send(message);
        }
        catch(Exception ex)
        {
            Trace.TraceError(String.Format("{3} : {4}", message.MessageId,topic,ex.Message));
        }
    }
}

Here quickly explained :

  • I serialize the object before i send it
  • I use a Steam object to prepare my object, in addition with a TextWriter to avoid bad surprises.
  • I prepare my BrokeredMessage which will be sent to my next Topic or Queue
  • I add additional properties to be able to easily Track / Debug the process.
  • And to finish, I obviously send the message.

Here is the process we are using today.
It's working for all types of entities, no need a special code based on the Accounts or Opportunities, with a bit more code, it's also possible for example to send the result to a different queue based on the Entity Type.

So the quick conclusion of those two articles is :

The CRM is really easy to configure to send data to the Azure Service Bus as you saw in the part 1.
Based on what you expect and want to achieve, there is a bit more work to do but that's still really fair compare to the amount of code we would need to produce to do the same process from scratch.

Happy CRM and Service Bus !

Clement

Dynamics 365 CRM & Power Platform addict.