AzureFunctions.Autofac 线程安全依赖注入问题

AzureFunctions.Autofac threadsafe dependency injection issue

我正在使用 AzureFunctions.Autofac 注入我的 Azure Functions Web api。配置示例:

  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 函数,我有一个 class 定义了 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.

我已确认 asyncawait 使用正确,因此遵循错误消息的建议并不能解决问题。问题似乎是 IDbContext 没有像预期的那样遵守 InstancePerLifetimeScope。发生这种情况是因为我的 InventoryFunctions class 中有不止一种方法吗?或者 AzureFunctions.Autofac 不是线程安全的?

将 DbContext 的注册更改为:

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

关于为什么会发生这种情况,您可以找到我的更深入的解释 here

我正在按照这个 SO 回答: 说 InstancePerLifetimeScope 是 non-ASP.NET 等同于 InstancePerRequest.

我和开发人员谈过,他们说事实是当你简单地使用 builder.RegisterType<SecretCompanyContext>.As<IDbContext>() 注册时,每个 HttpRequest 获得一个 DbContext 是默认行为,所以那里有一些错误信息。

所以解决方案是,而不是使用

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

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

一个人应该只用

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

如果目标是每个 HTTP 请求一个实例。