2014-11-24 59 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