自动注册通用 类

Auto-registration of generic classes

我正在尝试使用 Simple Injector 注入通用 class。 但是,它失败了

SimpleInjector.ActivationException: No registration for type ... could be found

这是重现它的代码:

using System;

namespace ErrorReproduction
{
    public class GenericClass<T>
    {
        public T Value { get; set; }
    }

    public interface IGenericInterface<T>
    {
        void DoSomething(T value);
    }

    public class NonGenericService : IGenericInterface<string>
    {
        public void DoSomething(string value) =>
            Console.WriteLine($"NonGeneric DoSomething (string): {value}");
    }

    public class GenericService<T> : IGenericInterface<GenericClass<T>>
    {
        public virtual void DoSomething(GenericClass<T> v) =>
            Console.WriteLine(
                $"Generic DoSomething {v.Value.GetType().Name}: {v.Value}");
    }

    /*
    public class StringService : GenericService<string>
    {
    }
    */

    public static class Test
    {
        public static void Main(string[] args)
        {
            var container = new SimpleInjector.Container();
            container.RegisterSingleton(
                typeof(IGenericInterface<>),
                typeof(IGenericInterface<>).Assembly);

            // works
            container.GetInstance<IGenericInterface<string>>()
                .DoSomething("non-generic-test");

            // breaks without StringService (No registration for type
            // IGenericInterface<GenericClass<string>>)
            container.GetInstance<IGenericInterface<GenericClass<string>>>()
                .DoSomething(
                    new GenericClass<string> {Value = "di-generic-test"});
        }
    }
}

如果我在上面的 StringService 中添加注释,它就可以工作。但是,如果没有它,我会收到以下异常:

Unhandled exception. SimpleInjector.ActivationException: No registration for type IGenericInterface<GenericClass<string>> could be found. at ErrorReproduction.Test.Main(String[] args) in ErrorReproduction/Test.cs:line 45

如果我创建一个本身不再通用的特定实现,DI 就可以工作。 但是,即使没有具体的,不抽象的GenericService<T>也足够了,应该是可注入的。

我不想为每个 GenericService<T> 案例创建特定的 class。它们会像上面那样是空的StringService,完全依赖通用服务的实现。

有没有办法让 DI 为这种情况工作?

编辑: 原来我的用例与我上面描述的不完全一样。但是,提供的评论和答案使我走上了正确的轨道。 最后,这对我有用:

container.RegisterSingleton(typeof(IMapper<,>), typeof(IMapper<,>).Assembly);
container.RegisterConditional(typeof(IMapper<,>),
    typeof(SimpleTimedValueProxyMapper<>),
    Lifestyle.Singleton,
    ctx => !ctx.Handled);
container.RegisterConditional(typeof(IMapper<,>),
    typeof(TimedValueProxyMapper<,>),
    Lifestyle.Singleton,
    ctx => !ctx.Handled);

鉴于第一个示例中的 classes,可能会解决这个问题(尚未测试):

container.RegisterSingleton(typeof(IGenericInterface<>), typeof(IGenericInterface<>).Assembly);
container.RegisterConditional(typeof(IGenericInterface<>),
    typeof(GenericService<>),
    Lifestyle.Singleton,
    ctx => !ctx.Handled);

RegisterXXX(Type, Assembly[]) 的 XML 文档说明如下(强调我的):

Registers all concrete, non-generic, public and internal types in the given set of assemblies

也就是说,默认情况下,只注册非泛型类型;您的 GenericService<T> 在此自动注册过程中被跳过。

这是设计使然,这样做是因为泛型通常需要特殊处理。那是因为它们很容易重叠,而且它们的注册顺序可能很重要。

因此,要解决此问题,您可以执行以下操作:

container.RegisterSingleton(
    typeof(IGenericInterface<>),
    typeof(IGenericInterface<>).Assembly);

container.RegisterSingleton(
    typeof(IGenericInterface<>), typeof(GenericService<>));

当有很多泛型类型,并且它们之间没有重叠时,您可以指示 Simple Injector 在注册中也包含泛型类型定义。这可以按如下方式完成:

container.Register(
    typeof(IGenericInterface<>),
    container.GetTypesToRegister(
        typeof(IGenericInterface<>),
        new[] { typeof(IGenericInterface<>).Assembly },
        new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true }));