Microsoft Dynamics 365 Blog

I am excited about the Microsoft’s Azure initiative. App Fabric is a part of Window Azure platform and as indicated at http://www.microsoft.com/windowsazure, makes it simpler to connect cloud and on-premise applications. It also simplifies the on-premise to on-premise application connection separated by firewalls and enables management of such interaction via the configurable rules of Access Control Service (ACS).

Consider a simple application which syncs accounts created in CRM system with another system. This application works in a classical polling model, where it polls CRM every so often for any newly added accounts. The polling interval may differ based on the other system requirements on how current the data needs to be represented. The higher the urgency, more the load it is going to put on CRM by repeatedly running the search query for new accounts.

It would be nice if the syncing module had some mechanism of being notified if a new account has been created, classical push model, which will allow it to only query CRM if needed and better if it can be notified with all the account details it needs to sync between the two systems. Lets try to see how we can alleviate the need of continuous polling with still maintaining the same data consistency between the two systems.

Pre-requisites

We have a functional CRM 4.0 system with App Fabric SDK installed, an account with the Windows Azure platform App Fabric and a program that polls CRM via the SDK to sync data every, say 15 minutes.

Our goal is to enhance the polling code to run search query against CRM only when new accounts are created and cut down on blind polling behavior.

To achieve our goals, we will write a plug-in in CRM that would post to the App Fabric any time a new account is created, The endpoint to which the data is posted by CRM is maintained by the application and it will track when was the last account in CRM created on.

Plug-in code

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.ServiceModel;

using Microsoft.Crm.Sdk;
using Microsoft.ServiceBus;

namespace AppFabricPlugin
{
    public class Plugin : IPlugin
    {
        // Variables
        private string SolutionName;
        private string ServicePath;
        private string MgmtKey;

        #region Constructor
        public Plugin(string config)
        {
            if (String.IsNullOrEmpty(config))
            {
                throw new InvalidPluginExecutionException("config can not null or empty.");
            }

            // Parse config.
            string[] parts = config.Split(new char[] { ';' });
            if (parts.Length == 3)
            {
                SolutionName = parts[0];
                ServicePath = parts[1];
                MgmtKey = parts[2];
            }
            else
            {
                throw new InvalidPluginExecutionException("Invalid config.");
            }
        }
        #endregion

        #region IPlugin Members
        public void Execute(IPluginExecutionContext context)
        {
            // Set mode to http (https).
            ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;

            // Address.
            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri(Uri.UriSchemeHttps, SolutionName, ServicePath);

            // Binding
            WS2007HttpRelayBinding binding = new WS2007HttpRelayBinding();
            binding.Security.Mode = EndToEndSecurityMode.Transport;

            // Create the channel factory.
            using (ChannelFactory<IServiceBusChannel> channelFactory = new ChannelFactory<IServiceBusChannel>(binding, new EndpointAddress(serviceUri)))
            {
                // Apply the auth behavior
                channelFactory.Endpoint.Behaviors.Add(RetrieveAuthBehavior());

                // Create and open the client channel
                using (IServiceBusChannel channel = channelFactory.CreateChannel())
                {
                    channel.Open();
                    // Use context correlationupdatetime to track the operation time.
                    channel.Execute(context.PrimaryEntityName + ";" + context.CorrelationUpdatedTime.Value);
                }
            }
        }
        #endregion

        #region Private members
        private TransportClientEndpointBehavior RetrieveAuthBehavior()
        {
            // Behavior
            TransportClientEndpointBehavior behavior = new TransportClientEndpointBehavior();
            behavior.CredentialType = TransportClientCredentialType.SharedSecret;
            behavior.Credentials.SharedSecret.IssuerName = "owner";
            behavior.Credentials.SharedSecret.IssuerSecret = MgmtKey;

            return behavior;
        }
        #endregion

        #region Contracts        
        [ServiceContract(Namespace = "http://schemas.microsoft.com/crm/2007/Contracts")]
        public interface IRemotePluginContract
        {
            [OperationContract(IsOneWay = true)]
            void Execute(string data);
        }

        public interface IServiceBusChannel : IRemotePluginContract, IClientChannel { }
        #endregion
    }
}

Service listener code

public void Main(string[] args)
{
    // Set mode to http (https).
    ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;

    // Address.
    Uri serviceUri = ServiceBusEnvironment.CreateServiceUri(Uri.UriSchemeHttps, SolutionName, ServicePath);

    // Binding
    WS2007HttpRelayBinding binding = new WS2007HttpRelayBinding();
    binding.Security.Mode = EndToEndSecurityMode.Transport;

    using (ServiceHost host = new ServiceHost(typeof(RemoteService)))
    {
        host.AddServiceEndpoint(typeof(IRemotePluginContract), binding, serviceUri);
                                host.Description.Endpoints[0].Behaviors.Add(RetrieveAuthBehavior());

        host.Open();

        Console.WriteLine("Press [Enter] to exit");
        Console.ReadLine();
    }
}

[ServiceBehavior]
public class RemoteService : IRemotePluginContract
{
    #region IRemotePluginContract Members
    public void Execute(string data)
    {
        // Do something.
    }
    #endregion
}

Plug-in registration

Compile and register the plug-in on account create. Register it to run asynchronously so that the post can happen independent of the actual create. In the config part add a semicolon separated string containing the solutionname;solutionpath;sharedkey

Configuring the ACS

We don’t need any rules configured in the ACS as we are using shared secret key to authenticate. All you would need to have is a solution name space on the App Fabric and use the management key. I have provided some links for you to get started.

With the above system in-place, the syncing module only needs to query CRM if the last account created on time is greater than then last time it synced. The model can be further improved and tweaked based to the requirements, but this simple demo enables us to see the integration empowerment that Windows Azure platform App Fabric enables. The best thing I like is it enables management of data syncing between the two systems.

References

If you are new to Windows Azure: http://www.microsoft.com/windowsazure/

If need more info on App Fabric: http://www.microsoft.com/windowsazure/appfabric/

If you to download App Fabric SDK: http://www.microsoft.com/downloads/details.aspx?FamilyID=39856a03-1490-4283-908f-c8bf0bfad8a5&displaylang=en

If you need more info on CRM plug-in registration tool:

Cheers,

Shashi Ranjan


We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!