C#与.NET Core:为DI创建的对象正确添加单次事件处理程序
问题分析与解决方案
你的代码存在无限递归调用的问题:在注册RestUtilityService的Transient服务时,工厂方法里调用了factory.GetService<RestUtilityService>(),这会再次触发同一个工厂方法,陷入无限循环,最终导致应用挂起。
要实现「每个RestUtilityService实例创建时仅订阅一次事件」的需求,同时避免递归问题,以下是两种可行方案:
方案一:直接在工厂中实例化服务
如果RestUtilityService的构造函数依赖可以通过服务提供者直接获取,直接创建实例并绑定事件即可,不会触发递归:
builder.Services.AddTransient(factory => { // 传入构造函数所需的所有依赖 var rest = new RestUtilityService( factory.GetRequiredService<IDependency1>(), factory.GetRequiredService<IDependency2>() // 其他依赖项 ); rest.Exception += Api_Exception; return rest; });
这种方式下,每个新创建的实例都会绑定一次事件,且仅绑定一次。
方案二:使用装饰器模式(面向接口场景)
如果你的服务是基于接口设计的(推荐做法),可以通过装饰器模式包裹原始服务,实现事件绑定,同时不修改原服务类:
- 定义接口与装饰类:
// 服务接口 public interface IRestUtilityService { event EventHandler<ExceptionEventArgs> Exception; // 其他服务方法 } // 原始服务实现 public class RestUtilityService : IRestUtilityService { public event EventHandler<ExceptionEventArgs> Exception; // 原服务逻辑实现 } // 装饰类,负责绑定事件 public class RestUtilityServiceDecorator : IRestUtilityService { private readonly IRestUtilityService _originalService; public RestUtilityServiceDecorator(IRestUtilityService originalService) { _originalService = originalService; // 仅在装饰类初始化时绑定一次事件 _originalService.Exception += Api_Exception; } // 委托事件与方法给原始服务 public event EventHandler<ExceptionEventArgs> Exception { add => _originalService.Exception += value; remove => _originalService.Exception -= value; } // 其他接口方法同理委托给原始服务 // public Task<ApiResponse> SendRequest(...) // { // return _originalService.SendRequest(...); // } }
- 注册服务:
// 先注册原始服务 builder.Services.AddTransient<IRestUtilityService, RestUtilityService>(); // 注册装饰后的服务,替换对外提供的实例 builder.Services.AddTransient<IRestUtilityService>(factory => { var original = factory.GetRequiredService<IRestUtilityService>(); return new RestUtilityServiceDecorator(original); });
特殊场景:全局仅订阅一次事件
如果你的需求是整个应用生命周期内仅订阅一次事件(无论创建多少服务实例),可以将服务改为单例:
builder.Services.AddSingleton(factory => { var rest = new RestUtilityService( factory.GetRequiredService<IDependency1>(), factory.GetRequiredService<IDependency2>() ); rest.Exception += Api_Exception; return rest; });
注意:单例服务会被所有请求共享,需要确保服务本身是线程安全的。
内容的提问来源于stack exchange,提问作者jstuardo
相关产品推荐
相关产品推荐

