Monday, June 10, 2013

Extending Microsoft Dynamics CRM online using Azure service bus

Using azure service bus, we can pass plug-in execution context from ms dynamics crm online to external line of business (LOB) applications.


 

MS CRM online  < - > Azure service bus <-> LOB application

 

Why..?
how..?

http://lakshmanindian.wordpress.com/2013/05/27/ms-crm-2011-integration-with-windows-azure/

refer the crm sdk for sample code of all contract types(onw way,two way,queue,etc.).


Create a Namespace in Windows Azure Service Bus

1.       login to Windows Azure
2.       Click to Service Bus

3.       Create a new namespace

      4.created namesapace will have the “Default Issuer” and “Default Key
 

Creating Service Endpoint & Configure ACS  

1.       Open the plugin registration tool and Click the “Register New service endpoint”
 

2.       Enter the following details

Solution Namespace –The namespace created in windows azure service bus
Path- any name
Contract- Two way

3.       Click “Save and configure ACS”


4.       Download the file the from CRM online Settings->Customization->Developer resources

5.       Enter the following details

Management Key- Default Key of namespace created in azure service bus
Certificate File- Upload the downloaded file (windows azure service bus issuer certificate)
Issuer name- name specifies in developer resources under customization  

6.       Click “Configure ACS”
7.       Click “Close”
8.       Click “Save and verify Authentication”
9.       Note down the “ID” of created endpoint  from plugin registration tool
Service Endpoint ID-“ID” of created endpoint

Create the plugin

1.       Create a plugin

public class CrmAzureIntegration : IPlugin
        {
        private Guid serviceEndpointId;
        Entity entity = null;

        public CrmAzureIntegration(string config)
            {
            if (String.IsNullOrEmpty(config) || !Guid.TryParse(config, out serviceEndpointId))
                {
                throw new InvalidPluginExecutionException("Service endpoint ID should be passed as config.");
                }
            }

        public void Execute(IServiceProvider serviceProvider)
            {
            // Retrieve the execution context.
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                // Obtain the target business entity from the input parameters.
                entity = (Entity)context.InputParameters["Target"];
                }

            // Extract the Organization Service
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            // Extract the tracing service.
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            if (tracingService == null)
                throw new InvalidPluginExecutionException("Failed to retrieve the tracing service.");

            IServiceEndpointNotificationService cloudService = (IServiceEndpointNotificationService)serviceProvider.GetService(typeof(IServiceEndpointNotificationService));
            if (cloudService == null)
                throw new InvalidPluginExecutionException("Failed to retrieve the service bus service.");

            try
                {
                tracingService.Trace("Posting the execution context.");
   // cloudService.Execute
                string response = cloudService.Execute(new EntityReference("serviceendpoint", serviceEndpointId), context);

                if (!String.IsNullOrEmpty(response))
                    {
                    // Update response back to the record
                   // throw new InvalidPluginExecutionException(response);
                   // UpdateResponse(response, (Guid)entity.Id, service);
                    entity["firstname"] = response;
                    }
                tracingService.Trace("Done.");
                }
            catch (Exception e)
                {
                tracingService.Trace("Exception: {0}", e.ToString());
                throw;
                }
            }

        public void UpdateResponse(string response, Guid recordId, IOrganizationService service)
            {
            Entity integrationEntity = new Entity("contact");
            integrationEntity["firstname"] = response;
            integrationEntity.Id = recordId;
            service.Update(integrationEntity);
            }

        }
2.       Register the plugin
3.       Pass the “created Service End point ID” under unsecured configuration


Create and Run the Listener Application

1.Create a console Application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using System.ServiceModel;
using Microsoft.ServiceBus;
using System.ServiceModel.Description;

namespace ConsoleApplication7
    {
    //class Program
    //    {
    //    static void Main(string[] args)
    //        {
    //        }
    //    }
    [ServiceBehavior]
   public class RemoteService : ITwoWayServiceEndpointPlugin
        {


        void ITwoWayServiceEndpointPlugin.Execute(RemoteExecutionContext executionContext)
            {
            //  throw new NotImplementedException();
            Utility.Print(executionContext);
            }
        //public string Execute(RemoteExecutionContext executionContext)
        //    {
        //   // throw new NotImplementedException();
        //    Utility.Print(executionContext);
        //    return "Hi";
        //    }
        }

    class Program
        {
        static void Main(string[] args)
            {
              ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;

            // Add service bus namespace
            string serviceNamespace = "<namespace name>";

            // Add Default issuer name
            string issuerName = "owner";

            // Add Service bus Default Key
            string issuerKey = "<Default issuer Key>";

            string servicePath = "<Path specified while registering service endpuint>";

            // Leverage the Azure API to create the correct URI.
            Uri address = ServiceBusEnvironment.CreateServiceUri(
                Uri.UriSchemeHttps,
                serviceNamespace,
                servicePath);

            // Create the shared secret credentials object for the endpoint matching the
            // Azure access control services issuer
            var sharedSecretServiceBusCredential = new TransportClientEndpointBehavior()
            {
                TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerKey)
            };

            // Using an HTTP binding instead of a SOAP binding for this endpoint.
            WS2007HttpRelayBinding binding = new WS2007HttpRelayBinding();
            binding.Security.Mode = EndToEndSecurityMode.Transport;

            // Create the service host for Azure to post messages to.
            ServiceHost host = new ServiceHost(typeof(RemoteService));
            host.AddServiceEndpoint(typeof(ITwoWayServiceEndpointPlugin), binding, address);

            ////host.AddServiceEndpoint(typeof(IServiceEndpointPlugin), binding, address);

            // Create the ServiceRegistrySettings behavior for the endpoint.
            var serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);

            // Add the service bus credentials to all endpoints specified in configuration.

            foreach (var endpoint in host.Description.Endpoints)
                {
                endpoint.Behaviors.Add(serviceRegistrySettings);
                endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
                }

            // Begin listening for messages posted to Azure.
            host.Open();
            Console.WriteLine("Press [Enter] to exit");
            Console.ReadLine();

            // Close the service.
            Console.Write("Closing the service host...");
            host.Close();
            Console.WriteLine(" done.");
            }
        }

    internal static class Utility
        {
        public static void Print(RemoteExecutionContext context)
            {
            Console.WriteLine("----------");
            if (context == null)
                {
                Console.WriteLine("Context is null.");
                return;
                }

            Console.WriteLine("UserId: {0}", context.UserId);
            Console.WriteLine("OrganizationId: {0}", context.OrganizationId);
            Console.WriteLine("OrganizationName: {0}", context.OrganizationName);
            Console.WriteLine("MessageName: {0}", context.MessageName);
            Console.WriteLine("Stage: {0}", context.Stage);
            Console.WriteLine("Mode: {0}", context.Mode);
            Console.WriteLine("PrimaryEntityName: {0}", context.PrimaryEntityName);
            Console.WriteLine("SecondaryEntityName: {0}", context.SecondaryEntityName);

            Console.WriteLine("BusinessUnitId: {0}", context.BusinessUnitId);
            Console.WriteLine("CorrelationId: {0}", context.CorrelationId);
            Console.WriteLine("Depth: {0}", context.Depth);
            Console.WriteLine("InitiatingUserId: {0}", context.InitiatingUserId);
            Console.WriteLine("IsExecutingOffline: {0}", context.IsExecutingOffline);
            Console.WriteLine("IsInTransaction: {0}", context.IsInTransaction);
            Console.WriteLine("IsolationMode: {0}", context.IsolationMode);
            Console.WriteLine("Mode: {0}", context.Mode);
            Console.WriteLine("OperationCreatedOn: {0}", context.OperationCreatedOn.ToString());
            Console.WriteLine("OperationId: {0}", context.OperationId);
            Console.WriteLine("PrimaryEntityId: {0}", context.PrimaryEntityId);
            Console.WriteLine("OwningExtension LogicalName: {0}", context.OwningExtension.LogicalName);
            Console.WriteLine("OwningExtension Name: {0}", context.OwningExtension.Name);
            Console.WriteLine("OwningExtension Id: {0}", context.OwningExtension.Id);
            Console.WriteLine("SharedVariables: {0}", (context.SharedVariables == null ? "NULL" :
                SerializeParameterCollection(context.SharedVariables)));
            Console.WriteLine("InputParameters: {0}", (context.InputParameters == null ? "NULL" :
                SerializeParameterCollection(context.InputParameters)));
            Console.WriteLine("OutputParameters: {0}", (context.OutputParameters == null ? "NULL" :
                SerializeParameterCollection(context.OutputParameters)));
            Console.WriteLine("PreEntityImages: {0}", (context.PreEntityImages == null ? "NULL" :
                SerializeEntityImageCollection(context.PreEntityImages)));
            Console.WriteLine("PostEntityImages: {0}", (context.PostEntityImages == null ? "NULL" :
                SerializeEntityImageCollection(context.PostEntityImages)));
            Console.WriteLine("----------");
            }

        #region Private methods.
        private static string SerializeEntity(Entity e)
            {
            StringBuilder sb = new StringBuilder();
            sb.Append(Environment.NewLine);
            sb.Append(" LogicalName: " + e.LogicalName);
            sb.Append(Environment.NewLine);
            sb.Append(" EntityId: " + e.Id);
            sb.Append(Environment.NewLine);
            sb.Append(" Attributes: [");
            foreach (KeyValuePair<string, object> parameter in e.Attributes)
                {
                sb.Append(parameter.Key + ": " + parameter.Value + "; ");
                }
            sb.Append("]");
            return sb.ToString();
            }

        private static string SerializeParameterCollection(ParameterCollection parameterCollection)
            {
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, object> parameter in parameterCollection)
                {
                if (parameter.Value != null && parameter.Value.GetType() == typeof(Entity))
                    {
                    Entity e = (Entity)parameter.Value;
                    sb.Append(parameter.Key + ": " + SerializeEntity(e));
                    }
                else
                    {
                    sb.Append(parameter.Key + ": " + parameter.Value + "; ");
                    }
                }
            return sb.ToString();
            }

        private static string SerializeEntityImageCollection(EntityImageCollection entityImageCollection)
            {
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, Entity> entityImage in entityImageCollection)
                {
                sb.Append(Environment.NewLine);
                sb.Append(entityImage.Key + ": " + SerializeEntity(entityImage.Value));
                }
            return sb.ToString();
            }
        #endregion
        }

2. Specify the Service bus namespace, Default issuer name, default issuer key and path
3. Run the console application
4. Trigger the plugin from CRM by creating the new contact record
5. Now the plugin execution context will be shown in the console application.