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

DryIoc 5.0.0条件注册INotify未匹配对应父服务问题

DryIoc 5.0.0 条件注册INotify实现的问题

环境与现有代码

我使用.NET Framework搭配DryIoc 5.0.0版本,现有以下类:

public class AMessageService : IHandle {
   public AMessageService (INotify notify) {
     //...
   }
}
public class BMessageService : IHandle {
   public BMessageService (INotify notify) {
     //...
   }
}

初始注册代码:

// ...
container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService));
container.Register<IHandle, BMessageService>(serviceKey: nameof(BMessageService));
// ...

可通过以下构造函数解析所有IHandle实现:

public SequentialEventAggregator(params IHandle[] handlers) // <-- 得到AMessageService和BMessageService

为支持多个INotify实现,创建NotifyComposite类:

public class NotifyComposite: INotify {
   private readonly IEnumerable<INotify> _notificators;
   public NotifyComposite(IEnumerable<INotify> notificators) {
        _notificators = notificators;
   }

   public void Notify() {
     _notificators.ToList().ForEach(n => n.Notify());
  }
}

更新后的注册代码:

// ...
container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService));
container.Register<IHandle, BMessageService>(serviceKey: nameof(BMessageService));

container.Register<INotify, NotifyComposite>();
// ...

问题描述

尝试注册带条件的多个INotify实现时出现异常:

// ...
container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService));
container.Register<IHandle, BMessageService>(serviceKey: nameof(BMessageService));

container.Register<INotify, NotifyComposite>();

// 使用Parent.Parent是因为第一个父级是NotifyComposite
container.Register<INotify, EmailChannelA>(setup: Setup.With(condition: r => r.Parent.Parent.ImplementationType == typeof(AMessageService)));
container.Register<INotify, EmailChannelB>(setup: Setup.With(condition: r => r.Parent.Parent.ImplementationType == typeof(BMessageService))); // <-- 无法正常工作
container.Register<INotify, ChatChannel>();
// ...

预期结果

  • 解析AMessageService时,notify为包含EmailChannelA和ChatChannel的NotifyComposite
  • 解析BMessageService时,notify为包含EmailChannelB和ChatChannel的NotifyComposite

当前结果

  • 解析AMessageService时,notify为包含EmailChannelA和ChatChannel的NotifyComposite
  • 解析BMessageService时,notify仍为包含EmailChannelA和ChatChannel的NotifyComposite

其他发现

  • 移除container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService))后功能正常,似乎setup.condition调用时解析上下文始终是第一个注册的IHandle,但原因不明
  • 尝试过openResolutionScope等属性,无效果
  • 查阅过DryIoc基于上下文的解析文档,但似乎解决的是不同问题
  • 该功能在旧版DryIoc(2.x.x)中可以正常工作
  • 在AMessageService和BMessageService中使用Made.Of会导致代码重复,我希望避免这种情况,比如后续所有不绑定条件、需注入到所有服务的INotify实现

解决方案

问题根源在于DryIoc 5.x版本中,依赖固定层级的Parent.Parent上下文判断不可靠,解析栈的层级可能因版本变化而改变。改用遍历整个解析栈的方式判断上下文,同时确保NotifyComposite每次解析都能收集到符合当前上下文的INotify实例:

// 先注册通用的ChatChannel(无绑定条件,所有服务都能获取)
container.Register<INotify, ChatChannel>();

// 注册EmailChannelA,条件为解析栈中存在AMessageService
container.Register<INotify, EmailChannelA>(setup: Setup.With(condition: r => 
    r.Stack.Any(s => s.ImplementationType == typeof(AMessageService))));

// 注册EmailChannelB,条件为解析栈中存在BMessageService
container.Register<INotify, EmailChannelB>(setup: Setup.With(condition: r => 
    r.Stack.Any(s => s.ImplementationType == typeof(BMessageService))));

// 注册NotifyComposite作为INotify的默认实现,显式指定Transient复用策略确保每次解析都重新收集实例
container.Register<INotify, NotifyComposite>(setup: Setup.With(reuse: Reuse.Transient));

// 最后注册IHandle实现
container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService));
container.Register<IHandle, BMessageService>(serviceKey: nameof(BMessageService));

说明

  1. 解析栈判断:使用r.Stack.Any()遍历整个解析上下文栈,检查是否包含目标服务类型,相比固定层级的Parent.Parent更稳定,不受DryIoc版本升级导致的解析链变化影响。
  2. Transient复用策略:显式指定NotifyComposite为Transient(DryIoc默认也是Transient,但显式声明更清晰),确保每次解析IHandle实例时,都会创建新的NotifyComposite并收集当前上下文符合条件的INotify实现。
  3. 注册顺序:先注册所有INotify实现,再注册NotifyComposite和IHandle,确保DryIoc能正确收集所有符合条件的实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:05:17