Search code examples
c#dynamics-crm

Dynamics CRM CrmServiceClient - how to connect to multiple organisations?


I have an ASP.Net web application which connects to Dynamics CRM using the XRM Tooling Connector. I pass in a connection string and create the connection like this:

 CrmServiceClient conn = new CrmServiceClient(ConnectionString);

This works fine if I am connecting to one Dynamics CRM organisation. However if I want to change my connection in the ASP.Net application, and call the service clients with a different connection string

CrmServiceClient conn = new CrmServiceClient(ConnectionString**2**);

The new connection string is ignored and the connection remains to the original connection string. This is due to the connection pooling, I assume.

If I call conn.Dispose() before setting the connection string - this makes no difference.

What does work is setting "requireNewInstance=True" in both connection strings. When I do this the web application is able to switch between organisations.. but the performance of the web app is terrible. Each page which would take <1s to load before would then take 30s or more to load as the connection is created from new each time.

Is there anyway to have two instances of CrmServiceClient alive at once, connected to two different CRM orgs? Or is there any way to force CrmServiceClent to be disposed and created new?


Solution

  • CrmServiceClient does not handle caching the connection for multiple connection strings. You are on your own to handle the caching part. Unless you have an unrestricted list this can usually be handled by having two objects.

    var org1SvcClient = new CrmServiceClient(org1ConnString);
    var org2SvcClient = new CrmServiceClient(org2ConnString);
    

    As long as you have the RequireNewInstance=true as part of each connection string you will have two different objects pointing to two different organizations.

    If you have lots of connections string you may want to use a dictionary to cache them so you can simply refer to them by the connection string text to get the right object.

    This is a quick sample what such a caching class would look like:

    public static class CrmServiceClientCache
    {
        private static Dictionary<string, CrmServiceClient> _internalCache = new Dictionary<string, CrmServiceClient>();
    
        private static Object _objLock = new object();
    
        public static CrmServiceClient Get(string connectionString)
        {
            if (_internalCache.ContainsKey(connectionString)) return _internalCache[connectionString];
            else
            {
                lock (_objLock)
                {
                    if (!_internalCache.ContainsKey(connectionString) && _internalCache[connectionString] != null)
                    {
                        var svcClient = new CrmServiceClient(connectionString);
                        if (svcClient.IsReady) _internalCache[connectionString] = svcClient;
                        else throw new Exception($"Failed to Successfully Create CrmServiceClient: {svcClient.LastCrmError}", svcClient.LastCrmException);
                    }
                    return _internalCache[connectionString];
                }
            }
        }
    }
    

    Then in your code you can do the following whenever you want an instance of CrmServiceClient for a specific connection string:

    Now you can call 'CrmServiceClientCache.Get(org1ConnString)' from anywhere in your code without worrying about whether it has been instantiated or not. If it hasn't, a new object will be instantiated and returned. And if it has been instantiated the existing object will be returned.

    Personal Opinion: The CrmServiceClient is very poorly documented in the SDK, it is not clear how it was intended to be used by developers. For example, what is the right way to handle multi-threading? It implements IDisposable but should it actually be wrapped in a using statement? And why would you want to dispose of it since the purpose is to maintain the connection/authentication across units of work. It's just confusing. Even the RequireNewInstance seems quirky.

    Performance Note: I'm not sure why you are getting such terrible performance. The time to authenticate shouldn't be taking more than a couple seconds. That is something I would research because it will cause production problems, as the authentication must be renewed and that renewal is probably going to happen during a page load - causing occassional performance hits. For example, running from my local machine the time to setup two CrmServiceClient objects was measured ~ 5-6 seconds.