Search code examples
.netdependency-injectiondryioc

DryIoc: setup condition yields unexpected parent


I am using DryIoc version 5.0.0 with .net framework and I have the following classes:

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

and the following registrations:

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

   // ...

I can resolve all available IHandle implementations in my constructor like this:

public SequentialEventAggregator(params IHandle[] handlers) // <-- yields AMessageService and BMessageService

Now I want to register multiple INotify implementations. I figured I can create a NotifyComposite class that gets an enumeration of INotify implementations

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

   public Notify():void {
     _notificators.ForEach(n => n.Notify());
  }
}
   // ...
   container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService));
   container.Register<IHandle, BMessageService>(serviceKey: nameof(BMessageService));

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

But now I am stuck trying to register multiple other INotify implementation, that have a setup condition.

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

   container.Register<INotify, NotifyComposite>();

   // using Parent.Parent because the first Parent is 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)); // <-- does not work properly
   container.Register<INotify, ChatChannel>();
   // ...

Expectation

  • When Resolving AMessageService --> notify is a NotifyComposite with the types EmailChannelA and ChatChannel
  • When Resolving BMessageService --> notify is a NotifyComposite with the types EmailChannelB and ChatChannel

Current Result

  • When Resolving AMessageService --> notify is a NotifyComposite with the types EmailChannelA and ChatChannel
  • When Resolving BMessageService --> notify is a NotifyComposite with the types EmailChannelA and ChatChannel

Other findings

  • When removing container.Register<IHandle, AMessageService>(serviceKey: nameof(AMessageService)) it works. Seems like, when the setup.condition function is called the resolution context is always the first registered IHandle, but I don't know why.
  • I played around with other properties like openResolutionScope, but that did not help
  • I also checked the context based resolution documentation in the DryIoc github, but that seemed to solve a different issue
  • This used to work in the older DryIoc version (2.x.x)
  • Using Made.Of in AMessageService and BMessageService would cause duplicate lines of code and I want to avoid that. E.g. for all further INotify implementations that are not tied to a condition and should be injected for all services)

How can to register this correctly?


Solution

  • I solved this issue: The problem must be due to caching and registering NotifyComposite without additional settings. Adding asResolutionCall or openResolutionScope to true for NotifyComposite ()causes the wanted behaviour.

    Fix: container.Register<INotify, NotifyComposite>(setup: Setup.With(asResolutionCall: true));