Невозможно заставить Dataverse ServiceClient правильно работать в функции Azure

У меня есть набор функций, запускаемых по HTTP, в приложении «Функции Azure» (.NET 8, изолированная рабочая модель), которые интегрируются с Dynamics 365 с помощью Dataverse ServiceClient (v1.1.17). Проблема, с которой я сталкиваюсь, заключается в том, что когда я отправляю несколько запросов, базовый HttpClient неожиданно удаляется.

Сначала я думал, что эта проблема связана с жизненным циклом ServiceClient. Тем не менее, я протестировал его, зарегистрировав как ограниченный, так и одноэлементный, но разницы не было. Я пробовал создать новый экземпляр и удалять его после каждого использования. Я даже попробовал «трюк», который нашел в репозитории GitHub CdsWeb, который оборачивает клиент и создает повторно используемую копию экземпляра.

Я запустил тот же код в новом минимальном API, и он работает отлично. То же самое касается запуска внутри консольного приложения внутри цикла. При нагрузочном тестировании с k6 (100 VU за 20 с) получается 1 завершенная и 99 прерванных итераций. Однако при реализации в минимальном API это приводит к 100 завершенным и 0 прерванным итерациям.

Запуск функции в Azure (на основе потребления) приводит к той же ошибке. Он также регулярно выдает TaskCanceledException.

Это минимальный тест:

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();

Вот как настраивается ServiceClient:

// 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;
}

См. строку подключения и тип аутентификации: Learn.microsoft.com/en-us/power-apps/developer/data-platform‌​/…

jdweng 14.04.2024 18:38

@jdweng Я не понимаю, что ты просишь меня показать? Я не использую строку подключения. Я обновил вопрос.

camlCase 14.04.2024 19:26

Вы отправляете HTTP-сообщение, в котором вы можете отправить только один запрос и получить один ответ при каждом соединении. Убедитесь, что вы получаете статус 200 OK с каждым сообщением, чтобы убедиться, что соединение закрывается правильно. Вы можете повторно использовать токен несколько раз (до истечения срока действия токена). Поэтому создайте токен, а затем цикл для каждого запроса. Либо вы получите ответ со статусом (200 ОК или ошибка 400/500), либо вы получите исключение о том, что соединение неожиданно закрылось. Запрос токена и запрос сообщения должны вернуть статус 200 OK.

jdweng 14.04.2024 21:33

Вы сказали, что это работает в Fresh API. Что такое целевая NET/CORE версия Fresh по сравнению с неудачной? В последнее время я видел много случаев сбоя Net 8. Вы пробовали Net 7?

jdweng 14.04.2024 21:38

Клиент службы внедряется как синглтон для повторного использования между несколькими запросами, а токен доступа кэшируется. Я делаю это неправильно здесь? Новый API также работает на .NET8. Я не тестировал его на .NET7 (пока).

camlCase 15.04.2024 00:15

Клиент можно использовать повторно, если вы вызываете конструктор с «новым». Я не могу сказать, как вы зацикливаетесь на опубликованном коде. Опубликованный код выглядит так, как будто он получает токен и только один клиент для каждого соединения вместо создания нового клиента для каждого соединения.

jdweng 15.04.2024 01:18

Не могли бы вы привести пример?

camlCase 15.04.2024 01:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
445
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Пакет NuGet Microsoft.PowerPlatform.Dataverse.Client версии 1.1.17 построен на .NET 6.0. Важно отметить, что он зависит от сборки System.Text.Json Version 7.0.0.0, которая является частью .NET 7. Однако .NET 7 недоступна на серверах функций Azure. См. это обсуждение на GitHub и на форумах сообщества Power Apps.

Простым решением было бы создать проект функции Azure на базе .NET 6 и использовать пакет NuGet Microsoft.PowerPlatform.Dataverse.Client версии 1.1.14 вместо последней.

Microsoft планирует добавить поддержку .NET 8 в конце этого года.

Я понизил проект функций до .NET 6 и сумел обработать 1504 запроса за 20 секунд. Ни один из них не был прерван, так что этот подход, похоже, работает. Спасибо! Я искал, но пропустил это обсуждение на форумах сообщества Power Apps. Однако я все еще сталкиваюсь с некоторыми удаленными исключениями, когда отменяю запрос до того, как функция вернула ответ. Я никогда не сталкивался с таким поведением в простом веб-API или в функции в целом.

camlCase 15.04.2024 14:08

Экземпляр ServiceClient не является потокобезопасным. Его нельзя использовать одновременно несколькими запросами. Используйте ServiceClient.Clone(), чтобы получить отдельные экземпляры IOrganizationService.

Henk van Boeijen 15.04.2024 15:53

Другие вопросы по теме