2014-11-24 57 views
3

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

我是新来的简单注射器(我使用Ninject,但在这种情况下,我们需要的东西,真的很快)。我使用了https://simpleinjector.readthedocs.org/en/latest/advanced.html#registering-plugins-dynamically文章,并设法将正确的dll加载到域中。

我货柜验证(container.Verify())期间,我目前正面临着一个奇怪的(在我看来)错误:

型GenericDAO的构造包含布尔类型的参数名为“isInUserContext”没有注册。请确保布尔已注册到容器中,或更改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类型的公共属性。

回答

6

异常消息是有点误导,因为简单的喷油器不允许你从注册在容器中的原始类型(如Boolean),所以其实容器建议你的东西是不可能做到的。

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

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

很难说你应该怎么做才能解决这个问题。一般来说,如果一个类型需要一些配置值,你需要明确告诉你的容器应该如何解析这种类型。有了简单的喷油器,这将如下所示:

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

但是,如果您有需要那些相同的配置值倍数(或多个)组件,这通常意味着你缺少的抽象。例如,isInUserContext可能是一个值,它应该隐藏在抽象的IUserContext后面,而connectionStringproviderName的抽象气味或类似的东西。在后一种情况下,GenericDAO课程将把创建连接的责任转移到不同的课程中,从而将注意力集中在它承担的任何责任上。

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

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

现在因为构造函数没有原始参数了,类现在可以自动通过有线您的容器。在简单的注射器如下您现在可以进行注册:

container.Register<GenericDAO>(); 

优点成为你的情况尤其明显,因为你是批量注册GenericDAO作为插件。所有批次注册的类型必须能够自动连线才能成功。

当然,你必须注册IUserContextIDbConnectionFactory为好,他们可能仍然依赖于某些基本价值观:

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

IUserContextIDbConnectionFactory抽象需要在一些中心组装被定义引导程序和插件程序集都参考。

另一种选择是让插件程序集自己注册插件。这些插件程序集需要依赖于你的DI容器。简单的注射器包含一个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>(...)混合使用。 A RegisterAll注册将回调集合中的每个元素的容器,因此通过注册具体类型Register<GenericDAO>可以明确指定简单注入器如何解析该具体类型。否则,简单注射器将尝试自动连线该具体类型并将其解析为瞬态。

+1

Steven,谢谢你的丰富解释。我会用你的建议。你关于缺失抽象的建议很好,但我太盲目了,看不到它。现在它真的有道理。再次感谢你。 – 2014-11-24 16:50:42