.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
相关产品推荐
相关产品推荐

