简单注入器-用参数注册每个类型
本文关键字:类型 注册 参数 注入器 简单 | 更新日期: 2023-09-27 18:12:04
我一直在使用Autofac作为DI容器,它很好,但是它有点太慢了。
所以我决定把我的项目迁移到Simple Injector,因为我看过一些基准测试,它是最快的一个。
它的API在我看来缺乏一点,但我相信有一些变通办法可以解决我的问题。
我正在扫描两个程序集以获得它们的类型,因为我想以自动的方式注册它们:
var repositories = (from type in typeof(DataRepository).Assembly.GetExportedTypes()
where type.Name.EndsWith("Repository")
select type).OrderBy(t => t.Name).ToList();
var repositioriesImp = (from type in typeof(SqlDataRepository).Assembly.GetExportedTypes()
where type.Name.EndsWith("Repository")
select type).OrderBy(t => t.Name).ToList();
所以我决定把它们的类型保存在字典中
var dictionary = repositories.Zip(repositioriesImp, (r, rImp) => new { r, rImp })
.ToDictionary(x => x.r, x => x.rImp);
在单个foreach循环中检索它们
foreach(var d in dictionary)
{
container.register(d.Key,d.Value,Lifestyle.Transient);
}
有一个问题: d值类型需要一个形参。
我知道我可以手工注册一个接一个,就像
container.Register<TService>(() => new TImplementation(connString));
但是使用DI容器的意义是什么,如果我必须做同样的事情,我将做与纯DI?
编辑作为参考,这是autoface的方法
container.RegisterAssemblyTypes(typeof(SqlDataRepository).Assembly)
.Where(t => t.Name.StartsWith("Sql"))
.As(t => t.BaseType)
.InstancePerLifetimeScope()
.WithParameter("connectionString", connectionString);
有几种方法可以解决这个问题,但我更倾向于回到问题的根源。你的问题的原因是缺少抽象。
虽然在服务中注入配置值(如连接字符串)是可以的(而另一方面注入运行时数据是不好的做法),但当你开始向多个服务注入相同的配置值时,你需要退后一步,检查应用程序的设计。
乍一看,一个存储库需要一个连接字符串,因为它连接到数据库,但是如果你仔细观察所有的存储库实现,你可能会发现在创建和打开连接时有很多重复的代码。这违反了DRY(可能还违反了OCP)。
解决方案是将创建和打开连接的逻辑提取到自己的服务中,并将其隐藏在抽象之后。一个很好的例子是使用CreateConnection()
方法来实现IConnectionFactory
抽象。
这样,您可以将connectionString
配置值隐藏在SqlConnectionFactory
实现后面,并且存储库将忽略此连接字符串。这减少了代码重复,使你的代码更容易阅读,你的应用程序更容易维护。
作为一个额外的好处,您获得了一个更容易维护的组合根和一个更容易配置的DI容器。这是因为您的存储库服务现在依赖于非常明确的IConnectionFactory
,而不是依赖于模糊的String
类型。
当然,你现在把问题转移到你的SqlConnectionFactory
,但现在这将是唯一的服务,依赖于该连接字符串,它可以注册如下:
container.RegisterSingleton<IConnectionFactory>(
new SqlConnectionFactory(connectionString));
请注意,开箱即用的简单注入器不支持WithParameter
之类的东西。正如您可能已经猜到的那样,这种支持的缺乏是明确的,因为我们喜欢激励开发人员来修复他们设计中的缺陷。不过,Simple Injector包含了允许您构建它的扩展点,比如使用IConstructorInjectionBehavior
接口。本文给出了使用IConstructorInjectionBehavior
可以做什么的详细示例。
顺便说一句,你的配置看起来很脆弱。在我看来,仅仅使用存储库实现进行注册要容易得多,也安全得多:
foreach (Type impl in repositioriesImp) {
container.Register(impl.GetInterfaces().Single(), impl);
}
顺便说一句,另一种使批处理注册更容易的方法是为存储库定义一个通用的IRepository<T>
抽象。这样,在Simple Injector中注册它们只需一行代码:
/// v3 syntax
container.Register(typeof(IRepository<>), new [] {
typeof(SqlDataRepository).Assembly });