Simple Injector – 使用参数注册foreach类型

我一直在使用Autofac作为DI Container,它很棒,但它有点太慢了。

所以我决定将我的项目迁移到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 => xr, x => x.rImp); 

在单个foreach循环中检索它们

 foreach(var d in dictionary) { container.register(d.Key,d.Value,Lifestyle.Transient); } 

有一个问题: d.Value类型需要一个参数

我知道我可以一个接一个地手动注册它们

 container.Register(() => new TImplementation(connString)); 

但是如果我必须做同样的纯DI工作,那么使用DI容器有什么意义呢?

编辑

作为参考,这是Autofac的方法

 container.RegisterAssemblyTypes(typeof(SqlDataRepository).Assembly) .Where(t => t.Name.StartsWith("Sql")) .As(t => t.BaseType) .InstancePerLifetimeScope() .WithParameter("connectionString", connectionString); 

有几种方法可以解决这个问题,但我更倾向于回到问题的根源。 你的问题的原因是缺少抽象。

尽管在服务中注入配置值(例如连接字符串)是好的(另一方面, 注入运行时数据是不好的做法 ,但是开始将相同的配置值注入多个服务的那一刻就是你需要退一步的时刻。检查应用程序设计。

乍一看,显然存储库需要连接字符串,因为它连接到数据库,但如果仔细查看所有存储库实现,您可能会在创建和打开连接时发现许多重复的代码。 这违反了DRY (可能还有OCP )。

解决方案是提取创建和打开连接到自己服务的逻辑,并将其隐藏在抽象之后。 一个很好的例子就是使用带有CreateConnection()方法的IConnectionFactory抽象。

这样,您可以隐藏SqlConnectionFactory实现后面的connectionString配置值,并且存储库将忽略此连接字符串。 这样可以减少代码重复,使您的代码更易于阅读,并且您的应用程序更易于维护。

作为额外的好处,您可以获得更容易维护的组合根和更容易配置的DI容器。 发生这种情况是因为您的存储库服务现在依赖于非常清晰的IConnectionFactory而不是依赖于模糊的String类型。

当然,您现在将问题移至SqlConnectionFactory ,但现在这将是唯一依赖于该连接字符串的服务,并且可以按如下方式注册:

 container.RegisterSingleton( new SqlConnectionFactory(connectionString)); 

请注意,开箱即用的Simple Injector不支持WithParameter 。 正如您可能已经猜到的那样,缺乏支持是明确的,因为我们希望激励开发人员修改他们设计中的缺陷。 尽管如此,Simple Injector包含允许您构建它的扩展点,例如使用IConstructorInjectionBehavior接口。 本文给出了使用IConstructorInjectionBehavior可以执行的操作的详细示例。

顺便说一句,你的配置似乎非常脆弱。 在我看来,使用存储库实现进行注册会更容易,更安全:

 foreach (Type impl in repositioriesImp) { container.Register(impl.GetInterfaces().Single(), impl); } 

顺便说一句,另一种使批量注册更容易的方法是为存储库定义一个通用的IRepository抽象。 这样,注册它们就是Simple Injector中的一个单行程序:

 /// v3 syntax container.Register(typeof(IRepository<>), new [] { typeof(SqlDataRepository).Assembly });