Friday, August 24, 2018

Dynamics 365 FO Connection to ServiceBus

I recently received a request to connect D365 FO to an Azure ServiceBus in order to download messages that were being sent by another application.  During the course of development, I found that there were 2 sets of projects that were developing connecting pieces: Microsoft.Azure.ServiceBus and Microsoft.ServiceBus.  The first component, however, was tied to .Net 4.6.1 and D365 FO is tied to .Net 4.5.2.  So, using the Azure component was out of the question.  However there was no documentation on how to use the object model in the straight ServiceBus.  Fortunately, both projects use a similar approach and a similar object model that I was able to make adaptations.  There were 2 caveats.


  1. D365 does not appear to work well with .Net async approaches.
  2. D365 does not appear to work well with generics.
In order to get around this, I used a synchronous approach and passed any element that required using generics to a C# class that could do the processing. The Service Bus dll can be downloaded from https://www.nuget.org/packages/WindowsAzure.ServiceBus/4.1.11

Once downloaded, open it as a zip file (you may need to change the extension to zip) and extract out the dll.  Then add the dll as a reference in D365.  Once there, the following class can be used to connect and download messages.  Sending messages would use a similar logic.  Please note that my requirements were to use Topics and Subscriptions rather than Queues.  Also, please note that I save all of the connection parameters in a separate table.  That way the system may access different Topic/Subscription combinations as required.  Also, if the connection information is changed, then this can be reflected in data.

public class ServiceBusConnect
{
    public void new()
    {  
    }

    public void receiveMessages(str _topic, str _subscription)
    {

        str strBody;
        System.IO.Stream stream;
        System.IO.StreamReader reader;
        Microsoft.ServiceBus.Messaging.SubscriptionClient subscriptionClient;
        Microsoft.ServiceBus.Messaging.MessagingFactory messagingFactory;
        Microsoft.ServiceBus.Messaging.BrokeredMessage message;
        ServiceBusConnectionParameters parameters;
       
        select firstonly parameters where parameters.Topic == _topic && parameters.Subscription == _subscription;
        ServiceBusConnector.BrokeredMessageProcessor connector = new ServiceBusConnector.BrokeredMessageProcessor();
        messagingFactory = Microsoft.ServiceBus.Messaging.MessagingFactory::Create(parameters.ConnectionString,
                Microsoft.ServiceBus.TokenProvider::CreateSharedAccessSignatureTokenProvider(parameters.SharedAccessKeyName, parameters.SharedAccessKey));

        subscriptionClient = messagingFactory.CreateSubscriptionClient(parameters.Topic, parameters.Subscription);
        message = subscriptionClient.Receive(System.TimeSpan::FromSeconds(5));
        if(message != null)
            setPrefix("Messages");

        while(message != null)
        {
            strBody = connector.GetBrokeredMessageContents(message);
            info(strBody);
            subscriptionClient.Complete(message.LockToken);
            message = subscriptionClient.Receive(System.TimeSpan::FromSeconds(5));
        }
        subscriptionClient.Close();
    }

}

The table ServiceBusConnectionParameters contains all the data for connection to Service Bus.  The Topic and Subscription should be based on how your project's service is configured.  The connection string will be sb://<your project>.servicebus.windows.net.  The SharedAccessKeyName will be what ever is configured in your service bus.  The default is RootManageSharedAccessKey.  The shared access key is the primary key as displayed on your Service Bus Explorer.

As usual for using .net assemblies in D365, all class references must be fully qualified with their name spaces.  The parameter in the subscriptionClient.Receive call is a timeout.  If no messages are found within 5 seconds, the call is aborted and the message is set to null.  Without it, the system just hangs indefinitely waiting for messages.  The call to GetBrokeredMessageContents uses a call to the GetBody method on the message itselft.  This method requires a generic reference to type the message.  I could not, however, find a way to do that within D365.  The wrapper to deserialize the message was pretty straightforward.

using System.IO;
using Microsoft.ServiceBus.Messaging;


namespace ServiceBusConnector
{
    public class BrokeredMessageProcessor
    {
        public string GetBrokeredMessageContents(BrokeredMessage message)
        {
            string strResult;

            using (var stream = message.GetBody<Stream>())
            {
                using (var reader = new StreamReader(stream))
                {
                    strResult = reader.ReadToEnd();
                }
            }

            return strResult;
        }
    }
}



No comments:

Post a Comment