There is one certainty in the world and that is that things don't stay the same! In the Dynamics 365 world, this is no exception, with new features and SDK features being released with a pleasing regularity. Writing 'revisited' posts has become somewhat of a regular thing these days.
In my previous post on this subject back in 2013 we looked at how you could use a connection dialog or connection strings to get a service reference from the Microsoft.Xrm.Client library and how it can be used in a thread safe way.
Microsoft.Xrm.Tooling
For a while now there has been a replacement for the Microsoft.Xrm.Client library – the Microsoft.Xrm.Tooling library. It can be installed from NuGet using:
Install-Package Microsoft.CrmSdk.XrmTooling.CoreAssembly
When you use the CrmServerLoginControl, the user interface should look very familiar because it's the same that is used in all the SDK tools such that Plugin Registration Tool.
The sample in the SDK shows how to use this WPF control.
The WPF control works slightly differently to the Xrm.Client ShowDialog() method – since it gives you much more flexibility over how the dialog should behave and allows embedding inside your WPF application rather than always having a popup dialog.
Connection Strings
Like the dialog, the Xrm.Tooling also has a new version of the connection string management – the new CrmServiceClient accepts a connection string in the constructor. You can see examples of these connection strings in the SDK.
CrmServiceClient crmSvc = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Xrm"].ConnectionString);
For Dynamics 365 online, the connection would be:
<connectionStrings>
<add name="Xrm" connectionString="AuthType=Office365;Username=jsmith@contoso.onmicrosoft.com; Password=passcode;Url=https://contoso.crm.dynamics.com" />
</connectionStrings>
Thread Safety
The key to understanding performance and thread safety of calling the Organization Service is the difference between the client proxy and the WCF channel. As described by the 'Improve service channel allocation performance' topic from the best practice entry in the SDK, the channel should be reused because creating it involves time consuming metadata download and user authentication.
The old Microsoft.Xrm.Client was thread safe and would automatically reuse the WCF channel that was already authenticated. The Xrm.Tooling CrmServiceClient is no exception. You can create a new instance of CrmServiceClient and existing service channels will be reused if one is available on that thread. Any calls the same service channel will be locked to prevent thread issues.
To demonstrate this, I first used the following code that ensures that a single CrmServiceClient is created per thread.
Parallel.For(1, numberOfRequests,
new ParallelOptions() { MaxDegreeOfParallelism = maxDop },
() =>
{
// This is run for each thread
var client = new CrmServiceClient(username,
CrmServiceClient.MakeSecureString(password),
"EMEA",
orgname,
useUniqueInstance: false,
useSsl: false,
isOffice365: true);
return client;
},
(index, loopState, client) =>
{
// Make a large request that takes a bit of time
QueryExpression accounts = new QueryExpression("account")
{
ColumnSet = new ColumnSet(true)
};
client.RetrieveMultiple(accounts);
return client;
},
(client) =>
{
});
With a Degree of Parallelism of 4 (the number of threads that can be executing in parallel) and a request count of 200, there will be a single CrmServiceClient created for each thread and the fiddler trace looks like this:
Now to prove that the CrmServiceClient handles thread concurrency automatically, I moved the instantiation into loop so that every request would create a new client:
Parallel.For(1, numberOfRequests,
new ParallelOptions() { MaxDegreeOfParallelism = maxDop },
(index) =>
{
// This is run for every request
var client = new CrmServiceClient(username,
CrmServiceClient.MakeSecureString(password),
"EMEA",
orgname,
useUniqueInstance: false,
useSsl: false,
isOffice365: true);
// Make a large request that takes a bit of time
QueryExpression accounts = new QueryExpression("account")
{
ColumnSet = new ColumnSet(true)
};
client.RetrieveMultiple(accounts);
});
Running this still shows a very similar trace in fiddler:
This proves that the CrmServiceClient is caching the service channel and returning a pre-authenticated version per thread.
In contrast to this, if we set the useUniqueInstance property to true on the CrmServiceClient constructor, we get the following trace in fiddler:
So now each request is re-running the channel authentication for each query – far from optimal!
The nice thing about the Xrm.Tooling library is that it is used exclusively throughout the SDK – where the old Xrm.Client was an satellite library that came from the legacy ADX portal libraries.
Thanks to my friend and fellow MVP Guido Preite for nudging me to write this post!
@ScottDurow