简单的喷油器 - 注册的foreach类型与参数 [英] Simple Injector - Register foreach type with parameter

查看:195
本文介绍了简单的喷油器 - 注册的foreach类型与参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用Autofac作为DI容器,它的伟大,但它是一个有点太慢了。



所以我决定把我的项目迁移到简单的喷油器,因为我已经看到了一些基准测试,它是最快的国家之一。



它的API但是缺乏在我看来一点,但我敢肯定,有一些解决方法为了解决我的问题。



我在扫描两个组件,以得到他们的类型,因为我想在一个自动化的方式来注册它们:

 变种库=(从(DataRepository).Assembly.GetExportedTypes()
型的typeof那里type.Name.EndsWith(库)
选择型).OrderBy(T => t.Name).ToList();
VAR repositioriesImp =(从(SqlDataRepository).Assembly.GetExportedTypes()
型的typeof那里type.Name.EndsWith(库)
选择型).OrderBy(T => t.Name).ToList();



所以我决定,以挽救他们的类型在字典

  VAR词典= repositories.Zip(repositioriesImp,(R,rImp)=>新建{R,rImp})
.ToDictionary(X => XR中,x => x.rImp);



来检索他们在一个单一的foreach循环

 的foreach(在字典变种D)
{
container.register(d.Key,d.Value,Lifestyle.Transient);
}

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



我知道我可以手动注册它们的 的一个个像

  container.Register< TService>(()=>新建TImplementation(CONNSTRING)); 



但有什么用DI容器如果我做同样的我会用纯做点DI?



修改



为参考,这里的Autofac办法做到这一点。

  container.RegisterAssemblyTypes(typeof运算(SqlDataRepository).Assembly)
。凡(T => t.Name.StartsWith(SQL) )
。至于(T => t.BaseType)
.InstancePerLifetimeScope()
.WithParameter(的connectionString的connectionString);


解决方案

有几种方法来解决这个问题,但我的偏好是回到你的问题的根源。你的问题的原因是缺少抽象。



虽然很纤细注入服务的配置值,如连接字符串(而在另一方面的注射运行时数据是不好的做法,瞬间启动注入相同的配置价值成多个服务是你需要做的退后一步,检查应用程序设计的时刻。



乍一看它可能看起来很明显,一个仓库需要一个连接字符串,因为它连接到数据库,但如果你在你所有的资源库实现仔细一看,你可能会发现大量的重复代码中创建和打开连接这违反的 DRY (也可能 OCP



的解决方案将是,以提取逻辑,用于创建和开口连接到它自己的服务和隐藏它的抽象的后面。这方面的一个很好的例子是有一个创建连接)的 IConnectionFactory 抽象(方法。



这样,您就可以隐藏 SqlConnectionFactory 实施和仓库后面的的connectionString 配置价值会无视这个连接字符串。这减少了代码重复,使你的代码更容易阅读和您的应用程序更易于维护。



作为一个额外的好处,你会得到一个组成根源,但更易于维护和DI容器,但更易于进行配置。这是因为你的资料库服务,现在取决于取决于暧昧字符串键入一个非常明确的 IConnectionFactory 代替。



当然,你现在进入的问题你的 SqlConnectionFactory ,但现在是唯一的服务,取决于该连接上字符串,它可以注册如下:

  container.RegisterSingleton< IConnectionFactory>(
新SqlConnectionFactory(的connectionString)) ;



请注意,外装即用,简单的喷油器有事情不支持 WithParameter 。正如你可能已经猜到了,这种缺乏支持是明确的,因为我们想鼓励开发商来解决他们的设计缺陷,而不是。尽管如此,简单的喷油器包含扩展点,允许你建立这一点,如使用 IConstructorInjectionBehavior 接口。 本文给出你可以做一个详细的例子 IConstructorInjectionBehavior



顺便说一句,你的配置显得非常脆弱。在我看来,它更容易,更安全只使用存储库实现了注册:

 的foreach(在repositioriesImp类型implement执行) (impl.GetInterfaces()单(),implement执行){
container.Register;
}



顺便说一句,另一种方式来进行批量注册更容易是定义一个通用 IRepository< T> 抽象的仓库。通过这种方式,将它们注册为一个班轮在简单的喷油器:

  /// V3语法
container.Register (typeof运算(IRepository<>),新的[] {
typeof运算(SqlDataRepository).Assembly});


I've been using Autofac as DI Container and it's great, but it's a bit too slow.

So I've decided to migrate my project to Simple Injector, because I've seen some benchmark tests and it's one of the fastest.

Its API however lack a bit in my opinion, but I'm sure there are some workaround to solve my problem.

I'm scanning two assemblies to get their types because I want to register them in an automated way:

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();

So I've decided to save their types in a dictionary

var dictionary = repositories.Zip(repositioriesImp, (r, rImp) => new { r, rImp })
            .ToDictionary(x => x.r, x => x.rImp);

to retrieve them in a single foreach loop

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

there is one problem tho: the d.Value types need a parameter.

I know I could register them by hand one by one like

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

but what's the point of using a DI Container if I have to do the same I would do with pure DI?

EDIT

for the reference, here's the Autofac way to do that

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

解决方案

There are several ways to address this issue, but my preference is to go back to the source of your problems. The cause of your problems is a missing abstraction.

Although it is fine to inject configuration values such as connection strings in services (while on the other hand injecting runtime data is bad practice, the moment to start injecting the same configuration value into multiple services is the moment you need to do a step back and check the application design.

At first sight it might look obvious that a repository needs a connection string, because it connects to the database, but if you look closely at all your repository implementations, you will probably find lots of duplicate code in creating and opening the connections. This is a violation of DRY (and possibly OCP).

The solution would be to extract the logic for creating and opening connections into its own service and hide it behind an abstraction. A good example of this is having an IConnectionFactory abstraction with a CreateConnection() method.

This way you can hide the connectionString configuration value behind the SqlConnectionFactory implementation and the repositories will be oblivious to this connection string. This reduces code duplication and makes your code easier to read and your application easier to maintain.

As an extra benefit you get a composition root that is much easier to maintain and a DI container that is much easier to configure. This happens because your repository services now depend on a very clear IConnectionFactory instead of depending on the ambiguous String type.

Of course you now move the problem to your SqlConnectionFactory, but that will now be the only service that depends on that connection string and it can be registered as follows:

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

Do note that out-of-the-box, Simple Injector has no support for things as WithParameter. As you might already guessed, this lack of support is explicit, since we like to motivate developers to fix flaws in their design instead. Still, Simple Injector contains extension points that allow you to build this, such as using the IConstructorInjectionBehavior interface. This article gives a detailed example of what you can do with IConstructorInjectionBehavior.

Btw, your configuration seems very fragile. Seems to me that it's much easier and safer to just use the repository implementations for the registration:

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

Btw, another way to make batch registration easier is to define a single generic IRepository<T> abstraction for repositories. This way, registering them is a one-liner in Simple Injector:

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

这篇关于简单的喷油器 - 注册的foreach类型与参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆