在Container中注册布尔值

时间:2014-11-24 14:44:30

标签: c# dependency-injection simple-injector

我正在重写我公司的DAO库。由于我们的解决方案的特性,我们需要根据客户切换ADO库(Oracle / SQL Server)。 由于静态引用是我们问题的主要来源(f.e.Oracle包取自CI而不是SQL Server),我决定采用插件架构,并尝试动态加载所需的dll。

我是Simple Injector的新手(我使用的是Ninject,但在这种情况下,我们需要一些非常快的东西)。我使用了https://simpleinjector.readthedocs.org/en/latest/advanced.html#registering-plugins-dynamically文章并设法将正确的dll加载到域中。

我目前在容器验证(container.Verify())期间遇到一个奇怪的(在我看来)错误:

  

GenericDAO类型的构造函数包含Boolean类型的参数,名称为'isInUserContext',未注册。请确保在容器中注册了Boolean,或者更改了GenericDAO的构造函数。

我的构造函数看起来像这样:

public class GenericDAO : DBHelper
{

    public GenericDAO(Boolean isInUserContext, string connectionString, string providerName, UniversalDataAccess universalDataAccess)
        : base(isInUserContext, connectionString, providerName, universalDataAccess)
    {
    }

我的注册:

var pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginAssemblies =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension.ToLower() == ".dll"
    select Assembly.LoadFile(file.FullName);

var pluginTypes =
    from assembly in pluginAssemblies
    from type in assembly.GetExportedTypes()
    where typeof (IDBHelper).IsAssignableFrom(type)
    where !type.IsAbstract
    where !type.IsGenericTypeDefinition
    select type;

_kernel.RegisterAll<IDBHelper>(pluginTypes); 

您知道如何成功初始化所有需要的类型吗? 我想公开GenericDAO类型的公共属性。

1 个答案:

答案 0 :(得分:6)

异常消息有点误导,因为Simple Injector不允许您在容器中注册原始类型(例如Boolean),所以实际上容器会告诉您一些不可能做的事情。

Simple Injector不允许您注册诸如int,bool,string之类的原语是因为这些类型是不明确的。 .NET中的DI容器根据类型信息注入服务,但是代码库中的所有组件在构造函数中需要string的实际上不太可能需要完全相同的值。有些人期望连接字符串,有些人期望一些文件路径。但是如果你能够在容器中注册string,你只能指定一个值(例如连接字符串),这样就无法注入另一个值(例如文件路径)。

然而,这个问题并不是Simple Injector特有的,这是一个普遍的问题。但是,其他DI容器可能允许您解析在构造函数中采用原始值的组件,并可能使用某个默认值填充此值。这几乎没用,因为如果你只需要那个原始类型的默认值,为什么还要通过构造函数公开这个类型呢?

很难说你应该做些什么来解决这个问题。一般来说,如果类型需要一些配置值,则需要明确告诉容器应该如何解析此类型。使用Simple Injector,这将如下所示:

container.Register<GenericDAO>(() => new GenericDAO(
    isInUserContext: true, 
    connectionString: "con str", 
    providerName: "System.Sql.Client",
    universalDataAccess: container.GetInstance<UniversalDataAccess>()));

但是,如果您有多个(或许多)组件需要相同的配置值,这通常意味着您缺少抽象。例如,isInUserContext可能是隐藏在IUserContext抽象背后的值,connectionStringproviderName气味就像IDbConnectionFactory抽象或其他东西类似。在后一种情况下,GenericDAO类将把创建连接的责任转移到另一个类中,使其能够专注于它所承担的责任。

在这种情况下,GenericDAO的构造函数将如下所示:

public GenericDAO(IUserContext userContext, IDbConnectionFactory connectionFactory,
    UniversalDataAccess universalDataAccess)

现在因为构造函数不再有原始参数,所以现在可以通过容器自动连接类。在Simple Injector中,您现在可以按如下方式注册:

container.Register<GenericDAO>();

在您的情况下,优势变得尤为明显,因为您批量注册GenericDAO作为插件。所有批次注册的类型必须是自动连线才能成功。

当然,您还必须注册IUserContextIDbConnectionFactory,它们可能仍然依赖于某些原始值:

container.RegisterSingle<IUserContext>(new AspNetUserContext());
container.RegisterSingle<IDbConnectionFactory>(
    new SqlServerDbConnectionFactory("con str"));

需要在引导程序和插件程序集引用的某个中央程序集中定义IUserContextIDbConnectionFactory抽象。

另一个选择是让插件程序集自己注册插件。除此之外,这些插件程序集需要依赖于您的DI容器。 Simple Injector包含一个SimpleInjector.Packaging NuGet项目,允许您在插件程序集中创建一个“包”。例如:

public class Plugin1Bootstrapper : IPackage
{
    public void RegisterServices(Container container) {
        container.Register<GenericDAO>(() => new GenericDAO(
            isInUserContext: true, 
            connectionString: "con str", 
            providerName: "System.Sql.Client",
            universalDataAccess: container.GetInstance<UniversalDataAccess>()));

        // other registrations here.
    }
}

在应用程序的启动路径中,您可以致电:

container.RegisterPackages(pluginAssemblies);

这将为所提供的程序集加载所有IPackage实现,并在每个程序集上调用RegisterServices

请注意,我们再次在此处注册GenericDAO的具体类型。您可以将其与您已使用的RegisterAll<IDBHelper>(...)混合使用。 RegisterAll注册将回调集合中每个元素的容器,因此通过使用Register<GenericDAO>注册具体类型,可以明确指定Simple Injector必须如何解析该具体类型。否则,Simple Injector将尝试自动连接该具体类型并将其解析为瞬态。