Introduction to Hsl.Xrm.Sdk.Client
The Hsl.Xrm.Sdk.Client namespace provides helpers for connection to Dataverse externally. It has improvements that can help improve scaling.
Using the IOrgServiceProvider
The starting point for the library is an IOrgServiceProvider
. This is a factory that creates service instances. To create an instance, use PoolingOrgServiceProvider
the constructor takes a connection string that matches XrmTooling but also has a additional AuthType option of HslClientSecret.
Console Application
static async Task Main(string[] args)
{
using (var pool = new PoolingOrgServiceProvider(connectionString)))
{
await pool.ValidateAllAsync();// Checks that each set of credentials is valid when using credential cycling.
await Task.WhenAll(args.Select(value =>
{
using (var svc = pool.Acquire())
{
return svc.Create(new Entity("account")
{
["name"] = value,
});
}
}));
}
}
DependencyInjection
This should generally be a singleton instance within your application. If you're using Microsoft.Extensions.DependencyInjection
, register the instance with .AddSingleton<IOrgServiceProvider>(sp => new PoolingOrgServiceProvider(connectionString, enableEarlyBoundTypes));
.
static async Task Main(string[] args)
{
using (var pool = new PoolingOrgServiceProvider(connectionString)))
{
await pool.ValidateAllAsync();// Checks that each set of credentials is valid when using credential cycling.
await Task.WhenAll(args.Select(value =>
{
using (var svc = pool.Acquire())
{
return svc.Create(new Entity("account")
{
["name"] = value,
});
}
}));
}
}
DependencyInjection
This should generally be a singleton instance within your application. If you're using Microsoft.Extensions.DependencyInjection
, register the instance with .AddSingleton<IOrgServiceProvider>(sp => new PoolingOrgServiceProvider(connectionString, enableEarlyBoundTypes));
.
Getting the Org Service
IOrgServiceProvider
provides a .Acquire()
method to get an instance of IProvidedOrgService
which is a wrapper interface for these three interfaces IDisposable
, IOrganizationService
, and IAsyncOrgService
. Be sure to wrap your code in a using statement or call .Dispose()
to return the object to the pool when completed. Use .Acquire(userId)
to enable impersonation.
using (var svc = orgServiceProvider.Acquire())
{
var whoAmI = (WhoAmIResponse) await svc.ExecuteAsync(new WhoAmIRequest()).ConfigureAwait(false);
}
⚠️ IOrgServiceProvider is thread safe.
IProvidedOrgService
instances returned from.Acquire()
are not. If running in parallel, make sure to call.Acquire()
within each parallel execution.
await Task.WhenAll(rows.Select(async (row) => {
using (var svc = orgServiceProvider.Acquire())
{
// GOOD
return await svc.CreateAsync(row).ConfigureAwait(false);
}
})).ConfigureAwait(false);
⚠️ Incorrect code
await Task.WhenAll(rows.Select(row => {
using (var svc = orgServiceProvider.Acquire())
{
// BAD - svc is Disposed before task completes
return svc.CreateAsync(row).ConfigureAwait(false);
}
})).ConfigureAwait(false);
⚠️ Incorrect code
using (var svc = orgServiceProvider.Acquire())
{
await Task.WhenAll(rows.Select(async (row) => {
// BAD - service is shared across parallel executions.
return await svc.CreateAsync(row).ConfigureAwait(false);
})).ConfigureAwait(false);
}
HslCurrentIdentity AuthType
The HslCurrentIdentity auth type will login using the identity executing the current set of code. This will work with Azure Managed Identity registered as a user in Dataverse.
To configure the login when running locally in Visual Studio, visit Tools > Options > Azure Service Authentication.
Sample connection string:
AuthType=HslCurrentIdentity; Url=https://myorg.crm.dynamics.com
HslClientSecret AuthType
HslClientSecret AuthType
The HslClientSecret auth type works similar to the ClientSecret auth type with the following differences:
- Supports true async instead of
Task.Run
wrappers for better scaling. - Allows specifying multiple app registrations for connection cycling.
- Requires specifying the Azure Active Directory Tenant Id.
- Allows specifying connection string setting
HandleAPILimitErrors=false
to disable automatic handling/retry when service protection limits are hit.
Sample connection string:
AuthType=HslClientSecret; Url=https://myorg.crm.dynamics.com; TenantId=46fb7dcf-398f-4f9b-838f-427c4926b872; AppId=cfd2b305-61af-4cb1-ab50-8af0fe86b9d0; AppKey=CLIENTSECRET
Sample connection string with app registration cycling. This will cause the app to cycle through app registrations using a new one for each request. This can help avoid issues with service protection limits in some scenarios.
⚠️ Be extremely cautious about specifying app registration cycling. It may be needed in some integrations but try to take other approaches to manage performance and throughput of your application prior to enabling this.
AuthType=HslClientSecret; Url=https://myorg.crm.dynamics.com; TenantId=46fb7dcf-398f-4f9b-838f-427c4926b872;
AppId1=cfd2b305-61af-4cb1-ab50-8af0fe86b9d0; AppKey1=CLIENTSECRET1;
AppId2=d8090a6f-370d-4d34-b4e2-82cc6a282f07; AppKey2=CLIENTSECRET2;
AppId3=59b00cc6-5685-4d88-a9ab-b21c495d3e77; AppKey3=CLIENTSECRET3;
AppId4=1d561c0c-abec-4283-8eb1-faa5b769ffc9; AppKey4=CLIENTSECRET4;
AppId5=cbb3f0a8-8ebe-48c6-88b4-f2635a1a672e; AppKey5=CLIENTSECRET5;
Maximizing throughput and scaling
Use AuthType=HslClientSecret in your connection string with Async methods. Use async/await instead of
.Result
or.Wait()
. See this article for an explanation of why this is important. Other auth types provide an async interface but these use Task.Run wrappers and will run into the thread exhaustion issues described in the article.In your appsettings, set
<add key="PreferConnectionAffinity" value="false" />
detail on whyAdd this code to the startup of your application.
// Change max connections from .NET to a remote service default: 2 System.Net.ServicePointManager.DefaultConnectionLimit = 65000; // Turn off the Expect 100 to continue message - 'true' will cause the caller to wait until it round-trip confirms a connection to the server System.Net.ServicePointManager.Expect100Continue = false; // Can decrease overall transmission overhead but can cause delay in data packet arrival System.Net.ServicePointManager.UseNagleAlgorithm = false;
If you're not using HslClientSecret for your auth type,
System.Threading.ThreadPool.SetMinThreads(100, 100);
can improve performance but using HslClientSecret with Async methods will be better.
HandleAPILimitErrors
HandleAPILimitErrors
By default, the SDK will catch service protection limit errors and wait until the server allows requests again to continue.
If hit, this can sometimes mean that requests will take minutes to complete. The same behavior exists in CrmServiceClient
from the official SDK.
To disable this functionality when using the HslClientSecret authtype, set HandleAPILimitErrors=false
in the connection string.