I have a set of HTTP-triggered functions in an Azure Functions app (.NET 8, isolated worker model) that integrate with Dynamics 365 using the Dataverse ServiceClient (v1.1.17). The issue I'm encountering is when I send multiple requests, the underlying HttpClient
gets disposed of unexpectedly.
At first, I thought this issue was related to the lifecycle of the ServiceClient
. However, I've tested it by registering it as both scoped and singleton, but there was no difference. I've tried creating a new instance and disposing of it after each use. I even tried a "trick" I found in the GitHub repo CdsWeb that wraps the client and creates a reusable instance copy.
I've run the same code in a fresh minimal API, and it works just fine. The same goes for running it inside a console app inside a loop. When load testing it with k6 (100 VUs for 20s) it results in 1 completed and 99 interrupted iterations. However, when implemented in a minimal API, it results in 100 completed and 0 interrupted iterations.
Running the function in Azure (consumption-based) produces the same error. It also regularly produces a TaskCanceledException
.
This is a bare minimum test:
var query = new QueryExpression(entityName: "contact")
{
TopCount = 1,
ColumnSet = new ColumnSet("contactid", "firstname")
};
// serviceClient injected as a singleton. Access token is cached in memory and reused.
var results = await serviceClient.RetrieveMultipleAsync(query);
var contactEntity = results.Entities.SingleOrDefault();
This is how the ServiceClient is setup:
// Adding it to servicecollection
services.AddSingleton<IOrganizationServiceAsync, ServiceClient>(provider =>
{
var appSettings = provider.GetRequiredService<IOptions<AppSettings>>();
var cache = provider.GetRequiredService<IMemoryCache>();
var managedIdentity = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
// Using Managed Identity running in Azure
ManagedIdentityClientId = appSettings.Value.ManagedIdentityClientId,
ExcludeManagedIdentityCredential = false,
// Using Visual Studio/VS Code credentials for local devlopment
ExcludeVisualStudioCredential = false,
ExcludeVisualStudioCodeCredential = false
});
var environment = appSettings.Value.DataverseInstanceUrl;
return new ServiceClient(
tokenProviderFunction: f => GetToken(environment, managedIdentity, cache),
instanceUrl: new Uri(environment),
useUniqueInstance: true);
});
// Getting access token and caching it for 50 minutes
async static Task<string> GetToken(string environment, DefaultAzureCredential credential, IMemoryCache cache)
{
var accessToken = await cache.GetOrCreateAsync(environment, async (cacheEntry) =>
{
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(50);
var token = await credential.GetTokenAsync(new TokenRequestContext([$"{environment}/.default"]));
return token;
});
return accessToken.Token;
}
NuGet package Microsoft.PowerPlatform.Dataverse.Client
version 1.1.17 is built on .NET 6.0. It is important to note that it has a dependency on assembly System.Text.Json Version 7.0.0.0
, which is part of .NET 7. However, .NET 7 is not available on Azure Functions servers. See this discussion on GitHub and on Power Apps Community Forums.
An easy fix would be to base your Azure Function project on .NET 6 and use NuGet package Microsoft.PowerPlatform.Dataverse.Client
version 1.1.14 instead of the latest.
Microsoft plans to add .NET 8 support at the end of this year.