自动注册通用 类
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 }));
我正在尝试使用 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 }));