AzureFunctions.Autofac — проблема внедрения потокобезопасных зависимостей

Я использую AzureFunctions.Autofac для внедрения в мой веб-API функций Azure. Пример конфига:

  public class DIConfig
    {
        public DIConfig()
        {
            DependencyInjection.Initialize(builder =>
            {
                // DAL
                builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerLifetimeScope();
                builder.RegisterType<SecretCompanyContext>().InstancePerLifetimeScope();
                builder.RegisterType<SecretCompanyContext>().As<ICartContext>().InstancePerLifetimeScope();
                builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

                // Services               
                builder.RegisterType<InventoryServices>().As<IInventoryServices>().InstancePerLifetimeScope();

                // Controllers ported from ASP.NET MVC Web API
                builder.RegisterType<InventoryController>().InstancePerLifetimeScope();
            });
        }

Затем мои функции Azure, у меня есть один класс, который определяет все методы в API.

    [DependencyInjectionConfig(typeof(DIConfig))]
    public class InventoryFunctions : FunctionsApi
    {
        [FunctionName("GetProductsByCategory")]
        // /inventory/categories/{id}/products
        public static async Task<HttpResponseMessage> GetProductsByCategory(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory/categories/{id}/products")]
            HttpRequestMessage req,
            TraceWriter log,
            int id,
            [Inject] InventoryController controller)
        {
            // do stuff
            var result = await controller.GetProductsByCategory(id);
            return JsonResponse(result, HttpStatusCode.OK);
        }

        [FunctionName("GetInventoryBySku")]
        // /inventory/skus?sku=ASDF&sku=ASDG&sku=ASDH
        public static async Task<HttpResponseMessage> GetInventoryBySku(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory")]
            HttpRequestMessage req,
            TraceWriter log,
            [Inject] InventoryController controller)
        {
            // do stuff
            var result = await controller.QueryInventoryBySkuList(skuList);
            return JsonResponse(result, HttpStatusCode.OK);
        }

        [FunctionName("UpdateProductsQuantity")]
        // /inventory
        // Post
        public static async Task<HttpResponseMessage> UpdateProductsQuantity(
            [HttpTrigger(AuthorizationLevel.Function, "put", Route = "inventory")]
            HttpRequestMessage req,
            TraceWriter log,
            [Inject] InventoryController controller)
        {
            // do stuff
            var inventoryProducts = await req.Content.ReadAsAsync<List<InvProductOperation>>();
            var result = await controller.UpdateAvailableProductsQuantity(inventoryProducts);
            return JsonResponse(result, HttpStatusCode.OK);
        }

Но я продолжаю получать эту ошибку:

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

Я проверил, что async и await используются правильно, поэтому следование рекомендациям в сообщении об ошибке не исправит ситуацию. Похоже, проблема в том, что IDbContext не соблюдает InstancePerLifetimeScope, как ожидалось. Это происходит из-за того, что в моем классе InventoryFunctions есть более одного метода? Или AzureFunctions.Autofac не является потокобезопасным?

Вы используете функцию Azure v1 или v2? v2 имеет встроенную поддержку DI.

Thomas 03.06.2019 14:14

@Thomas, использующий собственный IoC, ничего не изменит. Это зависит от области, используемой для регистрируемого DbContext.

alsami 03.06.2019 14:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
2
407
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Измените регистрацию DbContext на это:

 builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();

Вы можете найти мое более глубокое объяснение, почему это происходит здесь.

Если это не сработало для вас, вы должны создать другой контекст в этой регистрации IDbContext.

alsami 04.06.2019 21:54
Ответ принят как подходящий

Я исходил из этого SO-ответа: Autofac — InstancePerHttpRequest против InstancePerLifetimeScope, в котором говорилось, что InstancePerLifetimeScope был не-ASP.NET-эквивалентом InstancePerRequest.

Я говорил с разработчиками, и они сказали, что получение одного DbContext для каждого HttpRequest было поведением по умолчанию, когда вы просто регистрируетесь с помощью builder.RegisterType<SecretCompanyContext>.As<IDbContext>(), так что есть некоторая дезинформация.

Таким образом, решение заключается в том, чтобы вместо использования

builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();

или

builder.RegisterType<SecretCompanyContext>().As<IDbContext>().InstancePerLifetimeScope();

нужно просто использовать

builder.RegisterType<SecretCompanyContext>().As<IDbContext>();

если целью является один экземпляр на HTTP-запрос.

Так какое же решение в этом?

alsami 04.06.2019 21:53

@alsami См. выше.

devlord 05.06.2019 22:07

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