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

时间:2014-07-08 17:18:24

标签: c# linq dependency-injection simple-injector

我一直在使用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 => x.r, x => x.rImp);

在单个foreach循环中检索它们

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

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

我知道我可以手动注册 ,如

container.Register<TService>(() => 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);

1 个答案:

答案 0 :(得分:2)

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

虽然可以在服务中注入连接字符串等配置值(另一方面injecting runtime data is bad practice),但是开始将相同的配置值注入多个服务的那一刻就是你需要做的一步返回并检查应用程序设计。

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

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

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

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

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

container.RegisterSingleton<IConnectionFactory>(
   new SqlConnectionFactory(connectionString));

请注意,开箱即用的Simple Injector不支持WithParameter。正如您可能已经猜到的那样,缺乏支持是明确的,因为我们希望激励开发人员修改他们设计中的缺陷。尽管如此,Simple Injector包含允许您构建它的扩展点,例如使用IConstructorInjectionBehavior接口。 This article提供了使用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 });