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

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;
});

这种方式下,每个新创建的实例都会绑定一次事件,且仅绑定一次。

方案二:使用装饰器模式(面向接口场景)

如果你的服务是基于接口设计的(推荐做法),可以通过装饰器模式包裹原始服务,实现事件绑定,同时不修改原服务类:

  1. 定义接口与装饰类:
// 服务接口
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(...);
    // }
}
  1. 注册服务:
// 先注册原始服务
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:03:17