You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Functions中Cosmos DB Provider与MediatR的DbContext线程问题

问题:Azure Functions中使用EF Core Cosmos DB提供程序时的多线程访问异常

在Azure Functions中使用Entity Framework Core的Cosmos DB提供程序,当同时调用两个HTTP触发的函数时,收到DbContext被其他线程访问的异常。已尝试在AddDbContext()中设置Scoped和Transient生命周期,但问题仍然存在。

现有配置

  • 使用AddDbContext()注册并配置DbContext
  • UnitOfWork类依赖该DbContext,注册为Scoped服务
  • 多个MediatR的RequestHandler类依赖UnitOfWork,通过RegisterServicesFromAssembly()注册

Program.cs

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddDbContext<MyDbContext>(options =>
        {
            options.UseCosmos(
                "https://your-cosmosdb-uri",
                "your-cosmosdb-key",
                "your-database-name"
            );
        }, ServiceLifetime.Transient);

        services.AddTransient<IUnitOfWork, UnitOfWork>();

        services.AddMediatR(cfg =>
            cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)
        );
    })
    .Build();

host.Run();

MyFunctions.cs

public class MyFunctions
{
    private readonly IMediator _mediator;

    public MyFunctions(IMediator mediator)
    {
        _mediator = mediator;
    }

    [FunctionName("MyFunctionOne")]
    public async Task<IActionResult> RunOne(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        CancellationToken cancellationToken)
    {
        var result = await _mediator.Send(new MyRequest(), cancellationToken);
        return new OkObjectResult(result);
    }

    [FunctionName("MyFunctionTwo")]
    public async Task<IActionResult> RunTwo(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        CancellationToken cancellationToken)
    {
        var result = await _mediator.Send(new MyRequest(), cancellationToken);
        return new OkObjectResult(result);
    }
}

UnitOfWork.cs

public class UnitOfWork : IUnitOfWork
{
    private readonly MyDbContext _context;
    private readonly MyRepository _repo;
    
    public UnitOfWork(MyDbContext context)
    {
        _context = context;
        _repo = new MyRepository(_context);
    }

    public MyRepository ProductRepository { get => _repo; }
}

MyRequestHandler.cs

public class MyRequestHandler : IRequestHandler<MyRequest, MyResponse>
{
    private readonly IUnitOfWork _unitOfWork;

    public MyRequestHandler(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken)
    {
        // Perform operations using UnitOfWork or any other logic
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        // Create and return the response
        return new MyResponse
        {
            Result = $"Processed: {request.Data}"
        };
    }
}

问题原因分析

  1. Azure Functions的Scoped生命周期特殊性
    Azure Functions的工作者模型中,Scoped服务的生命周期并非严格绑定到单个请求——在函数实例复用的场景下,Scoped实例可能被多个请求共享。即使将DbContext设为Transient,若UnitOfWork或MediatR Handler的生命周期配置错误,仍会导致DbContext被跨线程复用。

  2. MediatR Handler的隐式复用风险
    虽然RegisterServicesFromAssembly()默认将RequestHandler注册为Transient,但如果Handler存在Singleton级别的依赖,或是函数运行时的实例复用机制导致Handler被保留,会间接让多个请求共享同一UnitOfWork和DbContext实例。

  3. DbContext本身的线程不安全特性
    EF Core的DbContext从设计上就不是线程安全的,Cosmos DB提供程序也遵循这一规则。当同一DbContext实例被多个并发线程调用时,必然触发线程访问异常。

  4. UnitOfWork的实例共享问题
    即使DbContext是Transient,若UnitOfWork注册为Scoped且函数实例被复用,多个请求可能拿到同一个UnitOfWork实例,进而共享其内部持有的DbContext。

内容的提问来源于stack exchange,提问作者Mouayed Bouameur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:03:22