How to read from Microsoft.Azure.ServiceBus queue populated from CRM 2016

Viewed 728

I am looking to create a .NET Core 2.2 console app that reads from a Azure Service Bus Queue. Messages are put on the queue by a CRM 2016 instance. The plan is to use the queue to notify externals when entities are created and/or updated.

I am able to read from the queue without issue using the following sample code.

var queueClient = new QueueClient(serviceBusConnectionString, queueName);
...
var messageHandlerOptions = new MessageHandlerOptions(this.ExceptionReceivedHandler) {
    MaxConcurrentCalls = 1,
    AutoComplete = false
};

queueClient.RegisterMessageHandler(this.ProcessMessagesAsync, messageHandlerOptions);
...

protected async Task ProcessMessagesAsync(Message message, CancellationToken token) {
    // do something ??? with message body
    byte[] body = message.Body;

    await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}

I have no problems reading from the queue and getting the message but I am unsure what to do next. All of the examples documentation I have found seems to work with an older version of the Azure Bus library and .NET Framework.

Once I have the Body property, what do I do with it? I assume I need to deserialize or cast it as a class but what class?

The samples on this site (https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/sample-one-way-listener) deal with the older library (https://www.nuget.org/packages/WindowsAzure.ServiceBus/). The samples include references to RemoteExecutionContext, BrokeredMessage (along with a GetBody() method) which does not seem to apply to newer library.

The samples on this site (https://github.com/Azure/azure-service-bus/tree/master/samples) are about Azure Bus in general and do not deal with CRM.

Am I using the correct library? Should I not be using Core? Can anybody point me to sample code that runs in .NET Core and interacts with queues and CRM?

2 Answers

The old ServiceBus SDK seems to do some deserialization for you, this you will need to do yourself now.

Looking at this example it seems that there is a Dynamics class called RemoteExecutionContext that is documented here and reading this it suggests you can set the data to be sent as JSON, looks like the default might be XML, but I've no idea.

If the data is sent as JSON you can deserialize by doing this:

protected async Task ProcessMessagesAsync(Message message, CancellationToken token) {

    var bytesAsString = Encoding.UTF8.GetString(message.Body);
    RemoteExecutionContext remoteExecutionContext = JsonConvert.DeserializeObject<RemoteExecutionContext>(bytesAsString);

    await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}

Note:

I had to install the nuget package Microsoft.Xrm.SDK.2015 to get a reference to the RemoteExecutionContext class, this package is build using .Net Framework so is not properly compatible with .Net Core

The default serializer that the old service bus library uses is still available under an "Interop" namespace, although it only wants a Stream so you need to convert the byte array

protected async Task ProcessMessagesAsync(Message message, CancellationToken token) {

    var deserializer = Microsoft.Azure.ServiceBus.InteropExtensions.DataContractBinarySerializer<RemoteExecutionContext>.Instance;

    using (var stream = new MemoryStream(message.Body))
    {
        RemoteExecutionContext remoteExecutionContext = deserializer.ReadObject(stream) as RemoteExecutionContext;
    }

    await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
Related