Link64
Link64

Reputation: 770

Resolve Services derived from base class using DryIoc

Context

Within my Solution I have multiple Projetcs that define an EntityFramework Core DbContext. In my Entrypoint Project I would like to inject all instances of DbContext to be able to apply pending migrations for each of them.

The contexts are registered through AddDbContext<T>(this IServiceCollection) and are copied into the DryIoc using Populate(this IContainer, IServiceCollection)

Example

class Base {};
class LeafOne : Base {};
class LeafTwo : Base {};

void Fun()
{
  var container = new Container();
  
  // Using Singleton to prove the point
  // This is meant to be a simplified version of the `AddDbContext` calls
  container.Register<LeafOne>(Reuse.Singleton);
  container.Register<LeafTwo>(Reuse.Singleton);

  container.ResolveMany<Base>(); // empty  
}

Problem

How can I properly register the Base to resolve both singleton instances?

Here is what I tried:

container.RegisterMapping<Base, LeafOne>();
container.RegisterMapping<Base, LeafTwo>();
container.ResolveMany<Base>(); // resolves only the LeafOne instance
container.Register<Base, LeafOne>(Reuse.Singleton);
container.Register<Base, LeafTwo>(Reuse.Singleton);
container.ResolveMany<Base>(); 
// resolves instances for both leaf classes, but -as expected- 
// they are different instances than the ones registered in
// the beginning
container.RegisterDelegate<Base>(ctx => ctx.Resolve<LeafOne>());
container.RegisterDelegate<Base>(ctx => ctx.Resolve<LeafTwo>());
container.ResolveMany<Base>();
// actually works for this example, but won't for DbContext because
// its trying to call a parameterless constructor for `Base` on resolve
// which doesn't exist

Is there a simple way to alias a type for it's parent class?

Upvotes: 1

Views: 592

Answers (1)

dadhi
dadhi

Reputation: 4950

The RegisterMapping should work but it won't in the current DryIoc <=4.3.4 because RegisterMapping uses the IfAlreadyRegistered.Keep by default, keeping the first Base registration and rejecting the second.

Here is the issue to fix it.

Until then you need to register with the service keys - it maybe even better in your specific case because it will hide the Base from the normal resolution but will keep them resolved or injected as a collection.

Here the code

using System;
using System.Linq;
using DryIoc;
                    
public class Program
{
    public static void Main()
    {
        var container = new Container();

        container.Register<LeafOne>(Reuse.Singleton);
        container.Register<LeafTwo>(Reuse.Singleton);
        
        // the keys are required in the DryIoc <=4.3.4 because RegisterMapping uses the IfAlreadyRegistered.Keep by default,
        // keeping the first Base registration and rejecting the second
        container.RegisterMapping<Base, LeafOne>(serviceKey: 1); 
        container.RegisterMapping<Base, LeafTwo>(serviceKey: 2);

        var bases = container.ResolveMany<Base>().ToArray();
        Console.WriteLine(bases.Length); // outputs 2
    }
    
    class Base {};
    class LeafOne : Base {};
    class LeafTwo : Base {};
}

Update: DryIoc v4.4 is released and RegisterMapping does not require service key

This should work now:

using System;
using System.Linq;
using DryIoc;
                    
public class Program
{
    public static void Main()
    {
        var container = new Container();

        container.Register<LeafOne>(Reuse.Singleton);
        container.Register<LeafTwo>(Reuse.Singleton);
        
        // Works in DryIoc v4.4
        container.RegisterMapping<Base, LeafOne>(); 
        container.RegisterMapping<Base, LeafTwo>();

        var bases = container.ResolveMany<Base>().ToArray();
        Console.WriteLine(bases.Length); // outputs 2
    }
    
    class Base {};
    class LeafOne : Base {};
    class LeafTwo : Base {};
}

Upvotes: 2

Related Questions