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

.NET Core 3.1部署Firebase仓库到服务器报错,本地运行正常

问题排查:部署后MediatR命令处理器构造失败报错

问题背景

基于Clean Architecture与CQRS模式开发的.NET Core 3.1 API,通过Firebase仓库向Firestore发送命令。本地运行正常,但部署到服务器后触发命令时出现构造处理器失败的错误。相关代码实现如下:

基础设施层Firebase仓库实现

namespace SmartRestaurant.Infrastructure.Services
{
    public class FirebaseConfig
    {
        public string BasePath { get; set; }
   
    }

    public class FirebaseRepository : IFirebaseRepository
    {
        readonly string _DataBaseBasepath;
        readonly FirestoreDb _db;
        public FirebaseRepository(IOptions<FirebaseConfig> conf)
        {
            _DataBaseBasepath = conf.Value.BasePath;

            if (string.IsNullOrEmpty(_DataBaseBasepath))
            {
                throw new ArgumentNullException("fireBase Path not found in appsettings");
            }
            var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
            Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
            _db = FirestoreDb.Create("^projectName");
        }

        public async Task<T> AddAsync<T>(string path, T data, CancellationToken cancellationToken)
        {
            try
            {
                DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
                var objectTosend = getOrderToDictionary(data);
                await doc.SetAsync(objectTosend, null, cancellationToken);
                return data;
            }
            catch (Exception exe)
            {
                throw exe;
            }
             
        }
      
        public async Task<T> UpdateAsync<T>(string path, T data, CancellationToken cancellationToken)
        {
            try
            {
                DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
                var objectTosend = getOrderToDictionary(data);             
                await doc.UpdateAsync(objectTosend, null, cancellationToken);
                return data;
            }
            catch (Exception exe)
            {
                throw exe;
            }
        }

    }
}

应用层接口定义

public interface IFirebaseRepository
{
    Task<T> AddAsync<T>(string path,T data,CancellationToken cancellationToken);
    
    Task<T> UpdateAsync<T>(string path,T data, CancellationToken cancellationToken);
}

基础设施层依赖注入配置

services.AddTransient < IFirebaseRepository, FirebaseRepository> ();

应用层命令处理器调用Firebase服务

public class OrdersCommandsHandlers : IRequestHandler<CreateOrderCommand, OrderDto>,
    IRequestHandler<UpdateOrderCommand, NoContent>,
  {
    private readonly IApplicationDbContext _context;
    private readonly IMapper _mapper;
    private readonly IUserService _userService;
    private readonly IFirebaseRepository _fireBase;
    private readonly string CreateAction = "CreateAction";
    private readonly string UpdateAction = "UpdateAction";

    public OrdersCommandsHandlers(IApplicationDbContext context, 
                                IMapper mapper,
                                IUserService userService,
                                IFirebaseRepository fireBase)
    {
        _context = context;
        _mapper = mapper;
        _userService = userService;
        _fireBase = fireBase;
    }

    public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        var validator = new CreateOrderCommandValidator();
        var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
        if (!result.IsValid) throw new ValidationException(result);

     ....

        await _context.SaveChangesAsync(cancellationToken);

        var orderDto = _mapper.Map<OrderDto>(order);
        orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);         
        var path = request.FoodBusinessId + "/Orders/" + orderDto.OrderId;
        await _fireBase.AddAsync(path, orderDto, cancellationToken);
            
      ......

        return _mapper.Map<OrderDto>(newOrder);
    }

   public async Task<NoContent> Handle(UpdateOrderCommand request, CancellationToken cancellationToken)
    {
        var validator = new UpdateOrderCommandValidator();
        var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
        if (!result.IsValid) throw new ValidationException(result);

   ......
        var orderDto = _mapper.Map<OrderDto>(order);
        var foodBusiness = await _context.FoodBusinesses.FindAsync(order.FoodBusinessId);
        if (foodBusiness != null)
            orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
        var path = order.FoodBusinessId + "/Orders/" + order.OrderId;
        await _fireBase.UpdateAsync(path,orderDto, cancellationToken);
        
        return default;
    }
}

报错信息

Error constructing handler for request of type MediatR.IRequestHandler`2[SmartRestaurant.Application.Orders.Commands.CreateOrderCommand,SmartRestaurant.Application.Common.Dtos.OrdersDtos.OrderDto]. Register your handlers with the container. See the samples in GitHub for examples.

可能的原因及解决方案

1. Firebase初始化时抛出未捕获异常,导致依赖注入容器无法构造FirebaseRepository

FirebaseRepository构造函数中存在多个服务器环境下可能失败的操作:

  • Firebase项目名称写死为^projectName,属于未替换的占位符
  • 服务器上jsonProjectConfogFile.json文件路径错误或不存在,AppContext.BaseDirectory在服务器环境的路径与本地不同
  • 设置GOOGLE_APPLICATION_CREDENTIALS环境变量失败,或文件权限不足

解决方案:

  • 替换FirestoreDb.Create("^projectName")中的占位符为真实的Firebase项目ID
  • 确保Firebase凭据文件被正确包含在发布包中:在项目属性中设置该文件的"复制到输出目录"为"始终复制"
  • 改用环境变量加载Firebase凭据,避免依赖本地文件路径:直接在服务器环境配置GOOGLE_APPLICATION_CREDENTIALS变量指向凭据文件,或使用Firebase Admin SDK的其他初始化方式
  • 在构造函数中添加日志记录,输出初始化过程的错误信息,方便定位具体问题:
    public FirebaseRepository(IOptions<FirebaseConfig> conf, ILogger<FirebaseRepository> logger)
    {
        _DataBaseBasepath = conf.Value.BasePath;
    
        if (string.IsNullOrEmpty(_DataBaseBasepath))
        {
            logger.LogError("Firebase BasePath not found in appsettings");
            throw new ArgumentNullException("fireBase Path not found in appsettings");
        }
        try
        {
            var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
            if (!File.Exists(pathConfigFile))
            {
                logger.LogError("Firebase credentials file not found at path: {Path}", pathConfigFile);
                throw new FileNotFoundException("Firebase credentials file not found", pathConfigFile);
            }
            Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
            _db = FirestoreDb.Create("your-real-project-id");
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Failed to initialize FirebaseRepository");
            throw;
        }
    }
    

2. MediatR命令处理器未被正确注册到依赖注入容器

本地运行正常但部署失败,可能是服务器环境下MediatR未扫描到命令处理器所在的程序集。

解决方案:

  • 在Startup/Program.cs中明确指定MediatR要扫描的程序集,确保包含OrdersCommandsHandlers:
    // .NET Core 3.1 Startup.cs
    services.AddMediatR(typeof(CreateOrderCommand).Assembly);
    
  • 检查发布配置,确保包含命令处理器所在的应用层程序集,没有被排除在发布之外

3. 命令处理器的依赖服务无法构造

OrdersCommandsHandlers依赖IApplicationDbContext、IMapper、IUserService等服务,若其中某个服务在服务器环境下无法初始化,会导致连锁失败,表现为MediatR构造处理器报错。

解决方案:

  • 检查这些依赖服务的注册代码,确保服务器环境下所有配置(如数据库连接字符串)正确
  • 启用依赖注入容器的验证,在启动时提前发现构造失败的问题:
    // 在Startup.cs的ConfigureServices末尾添加
    var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
    

4. 构造函数中执行了过重的外部依赖初始化

在构造函数中直接初始化FirestoreDb属于较重的外部操作,一旦失败会直接导致容器无法创建实例。

解决方案:

  • 改用延迟初始化,将FirestoreDb的初始化延迟到第一次使用时:
    private Lazy<FirestoreDb> _lazyDb;
    public FirebaseRepository(IOptions<FirebaseConfig> conf, ILogger<FirebaseRepository> logger)
    {
        _DataBaseBasepath = conf.Value.BasePath;
        if (string.IsNullOrEmpty(_DataBaseBasepath))
        {
            logger.LogError("Firebase BasePath not found in appsettings");
            throw new ArgumentNullException("fireBase Path not found in appsettings");
        }
        _lazyDb = new Lazy<FirestoreDb>(() =>
        {
            var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
            Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
            return FirestoreDb.Create("your-real-project-id");
        });
    }
    
    // 使用时通过_lazyDb.Value获取实例
    

内容的提问来源于stack exchange,提问作者rahim soufiane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:30:52