使用 DryIoc 解析从基础 class 派生的服务

Resolve Services derived from base class using DryIoc

上下文

在我的解决方案中,我有多个定义 EntityFramework 核心 DbContext 的项目。在我的入口点项目中,我想注入 DbContext 的所有实例,以便能够为每个实例应用挂起的迁移。

上下文通过 AddDbContext<T>(this IServiceCollection) 注册并使用 Populate(this IContainer, IServiceCollection)

复制到 DryIoc

例子

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  
}

问题

如何正确注册 Base 以解析两个单例实例?

这是我尝试过的:

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

有没有一种简单的方法可以为其父类型添加别名 class?

RegisterMapping 应该可以,但在当前的 DryIoc <=4.3.4 中不行 因为 RegisterMapping 默认使用 IfAlreadyRegistered.Keep,保留第一个 Base 注册并拒绝第二个。

这里是the issue to fix it.

在那之前您需要使用服务密钥注册 - 在您的特定情况下可能更好 因为它会隐藏 Base 从正常分辨率但会将它们作为集合解析或注入。

这里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 {};
}

更新:DryIoc v4.4 发布,RegisterMapping 不需要服务密钥

现在应该可以工作了:

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