In the last post in this series I showed you the difference between the standard OrganizationService and the Microsoft.Xrm.Client.CrmOrganizationService. Continuing with the subject of elaborating on the 'Developer Extensions' part of the Dynamics CRM SDK this post describes more about the 'Simplified Connection Management' features.
When accessing the IOrganizationService Inside a Plugin or Workflow all connection management is handled for you by the existing context:
// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
(See the SDK Sample for more on how to do this)
There are no server Urls and usernames or passwords to worry about since the code is already running in the context of Dynamics CRM.
Authentication with no help
When it comes to writing .NET Client or Portal Applications there isn't an existing context to use and so connection details must be stored and used when connecting to Dynamics CRM. The most basic approach using the standard Microsoft.Xrm.Sdk assembly would be:
// Active Directory
Uri organizationUri = new Uri("http://servername/orgname");
ClientCredentials credentials = new ClientCredentials();
credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
OrganizationServiceProxy proxy = new OrganizationServiceProxy(organizationUri, null, credentials, null);
// Claims Base Authentication
Uri organizationUri = new Uri("https://orgname.api.crm4.dynamics.com/XRMServices/2011/Organization.svc");
AuthenticationCredentials authCredentials = new AuthenticationCredentials();
authCredentials.ClientCredentials.UserName.UserName = username;
authCredentials.ClientCredentials.UserName.Password = password;
// Dynamics CRM Online requires device credentials as well
ClientCredentials deviceCredentials = new AuthenticationCredentials();
deviceCredentials.ClientCredentials.UserName.UserName = deviceUsername;
deviceCredentials.ClientCredentials.UserName.Password = devicePassword;
ClientCredentials credentials = authCredentials.ClientCredentials;
OrganizationServiceProxy proxy = new OrganizationServiceProxy(organizationUri, null, credentials, deviceCredentials);
Whilst this technique works well, you need to code around the following issues:
- Different Connection Types - Different code is required for Claims Based/Online/Active Directory authentication, so your code will need to detect which environment and authenticate accordingly.
- Connection Dialog - Creating a user interface for entering server addresses, user credentials and organization selection is a complex area with many possible configurations depending on your target environment.
- Thread Safety - The OrganizationServiceProxy is not thread-safe. Each thread will need a separate instance and downloading metadata & authenticating each time results in slow response times.
- Token Expiry - Claims Based authentication creates an authentication token that has a finite lifetime. Each time your code uses an OrganizationServiceProxy that has been cached, it will have to check and renew the token if required.
- Enable Proxy Types – If you need early bound support you'll need to remember to call 'EnableProxyTypes'
There are some helper classes in the SDK that help you but it's far from simple:
- Different Connection Types - Microsoft.Crm.Services.Utility.DeviceIdManager provides an easy way to automatically retrieve or create new device credentials when authenticating with Microsoft Dynamics CRM Online. See the authentication helper sample in the SDK.
- Connection Dialog - Microsoft.Crm.Sdk.Samples.ServerConnection class provides a way of prompting the user for Server information, but only works on the Command Line - ServerConnection Helper Class . There is also http://connectioncontrol.codeplex.com/ from Tanguy that provides a User interface for collection connection information
- Token Expiry - Microsoft.Crm.Sdk.Samples.ManagedTokenOrganizationServiceProxy provides a wrapped OrganizationServiceProxy that automatically checks and renews the Security Token each time it is created.
- Thread Safety - Microsoft.Xrm.Sdk.Client.IServiceManagement<IOrganizationService> provides a threadsafe way of getting an instance of an OrganizationServiceProxy. See 'Improve Service Channel Allocation Performance'. You can learn more about multi-threading with the OrganizationServiceProxy from Crm In The Field.
There is an easy way - Microsoft.Xrm.Client
Not to be confused with the Microsoft.Xrm.Sdk.Client, the Microsoft.Xrm.Client namespace is a single library designed specifically to make the Developer's task for writing rich .NET Client and ASP.NET applications easy. In part 1, we've already learned about the enhanced CrmOrganizationServiceContext, but this library also provides a simple way of connecting to Dynamics CRM that addresses all of the issues described above.
Connection Strings
Each connection can easily be expressed as a single connection string stored in the App/Web.Config, collected through a simple to use Connection Dialog or stored by your own custom configuration mechanism. This connection string is uses to create a CrmConnection, and from that you can create a OrganizationService.
// Connect with Dialog
ConnectionDialog connectionDialog = new ConnectionDialog();
bool? connected = connectionDialog.ShowDialog();
if (connected!=null && connected.Value)
{
string connectionString = connectionDialog.ConnectionString;
using (OrganizationService proxy = new OrganizationService(CrmConnection.Parse(connectionString)))
{
WhoAmIRequest request = new WhoAmIRequest();
WhoAmIResponse response = (WhoAmIResponse)proxy.Execute(request);
}
}
// Connect from App/Web.config
CrmConnection connection = new CrmConnection("CRM");
using (OrganizationService proxy = new OrganizationService(connection))
{
WhoAmIRequest request = new WhoAmIRequest();
WhoAmIResponse response = (WhoAmIResponse)proxy.Execute(request);
}
In your app/web.config file you can then add set of named connection strings. This works well for ASP.NET portal applications.
<configuration>
<connectionStrings>
<add name="CRM" connectionString="..."/>
</connectionStrings>
</configuration>
Thread Safety and Token Expiry
The good news is that all the token expiry and thread safety is handled for you automatically provided you reuse an instance of the CrmConnection each time you create an OrganizationService:
using (OrganizationService proxy = new OrganizationService(cachedConnection))
{
using (CrmOrganizationServiceContext ctx = new CrmOrganizationServiceContext(proxy))
{
}
}
The authentication and metadata requests will only happen on the first use and then once the auth token has expired (if you are using Claims Based/Online authentication).
CrmConnection Settings
As well as the connection string, you can also change the behaviour of the CrmConnection using the following settings:
- ProxyTypesEnabled – Defaults to true and controls if 'EnableProxyTypes' is called for your on your OrganizationServiceProxys.
- Timeout – You can control the timeout of your connections otherwise the default OrganizationServiceProxy timeout is use of 2 minutes.
- UserTokenExpiryWindow – null by default, but controls how often your security token should be renewed for Claims Based/Online authentication. If you leave as null, the authentication token 'ValidTo' will be used to define when the token should be renewed. Only set this value if you want to renew before the token has expired. Use with caution!
- ServiceConfigurationInstanceMode – Defaults to PerName, but can be modified to be PreInstance, PerRequest or Static. I recommend sticking with the default. If you use 'PerRequest' you will get a metadata and authentication request for every single proxy!
If you are writing .NET client or ASP.NET that connect to Dynamics CRM there is no reason not to use this library! Next up in part 3 is the super cool client side data caching mechanism provided by Microsoft.Xrm.Client.
@ScottDurow