Show / Hide Table of Contents

    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 why

    • Add 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.

    Back to top Hsl.Xrm.Sdk