2014-03-24 65 views
4

我在.NET 4.5中構建了一個系統,它將有不同的實現(即在不同的客戶的前提下實現)。每個客戶都將擁有自己的基礎架構和數據庫結構,因此我非常依賴洋蔥架構構建系統,本身依賴於接口和DI。通過這種方式,我可以使用客戶特定的「存儲庫」和「服務」實現。簡單的噴油器插件

我的目標是,無需重新編譯,能夠在一個客戶的服務器上安裝的系統(該系統入口點基本上是週期性發射了包含Windows服務的業務邏輯,同時還託管WCF服務)。爲了達到這個目的,我想到的是某種「Dependencies」或「Plugins」文件夾作爲包含Windows服務可執行文件的文件夾的子文件夾,該文件夾將包含一個客戶特定的DLL,該DLL具有實現所有必要接口的具體類應用程序依賴於它。

我試圖用簡單的噴油器來實現這一目標。我看了一下SimpleInjector.Packaging程序集以及關於「動態註冊插件」的段落here,但我仍然困惑,不知道從哪裏開始,比如我應該在哪個程序集中定義什麼。

我需要的是關於如何實現這一目標的一些具體的樣品。

被用於此目的的SimpleInjector包裝組裝,或會出現這個問題?如果是這樣,怎麼樣?

有人請賜教。

由於

PS:100%清楚:接口和具體實現顯然分離成不同的組件。這個問題是關於如何使用Simple Injector動態連接所有東西。

+0

此問題僅限於Simple Injector? – aevitas

+0

您是否閱讀過[本維基頁面](https://simpleinjector.codeplex.com/wikipage?title=Advanced-scenarios#Plugins)?我認爲它更接近你想要實現的目標。 – Steven

+0

@aevitas不,我在開發階段的這個時候使用Simple Injector,但如果您對另一個DI/IOC工具很容易實現這一點有任何建議,請隨時提及它們。我是DI/IOC初學者,所以我對任何事情都很開放。 – tjeuten

回答

5

結合IoC容器這樣做,如簡單噴油器的優點在於它是很容易的共同邏輯添加到所有的插件。我最近寫了一個批量圖像轉換工具,允許插入新的圖像轉換器。

這是接口

public interface IImageConverter : IDisposable 
{ 
    string Name { get; } 
    string DefaultSourceFileExtension { get; } 
    string DefaultTargetFileExtension { get; } 
    string[] SourceFileExtensions { get; } 
    string[] TargetFileExtensions { get; } 
    void Convert(ImageDetail image); 
} 

註冊像這樣做(注意錯誤檢查的插件依賴不在.NET程序集)的所有插件需要

private void RegisterImageConverters(Container container) 
{ 
    var pluginAssemblies = this.LoadAssemblies(this.settings.PluginDirectory); 

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

    container.RegisterAll<IImageConverter>(pluginTypes); 
} 

private IEnumerable<Assembly> LoadAssemblies(string folder) 
{ 
    IEnumerable<string> dlls = 
     from file in new DirectoryInfo(folder).GetFiles() 
     where file.Extension == ".dll" 
     select file.FullName; 

    IList<Assembly> assemblies = new List<Assembly>(); 

    foreach (string dll in dlls) { 
     try { 
      assemblies.Add(Assembly.LoadFile(dll)); 
     } 
     catch { } 
    } 

    return assemblies; 
} 

通用邏輯-in操作由裝飾者處理

container.RegisterDecorator(
    typeof(IImageConverter), 
    typeof(ImageConverterChecksumDecorator)); 
container.RegisterDecorator(
    typeof(IImageConverter), 
    typeof(ImageConverterDeleteAndRecycleDecorator)); 
container.RegisterDecorator(
    typeof(IImageConverter), 
    typeof(ImageConverterUpdateDatabaseDecorator)); 
container.RegisterDecorator(
    typeof(IImageConverter), 
    typeof(ImageConverterLoggingDecorator)); 

該最後一類更具體b UT我將它作爲你如何能找到所需要的插件爲例,無需將容器

public sealed class PluginManager : IPluginManager 
{ 
    private readonly IEnumerable<IImageConverter> converters; 

    public PluginManager(IEnumerable<IImageConverter> converters) { 
     this.converters = converters; 
    } 

    public IList<IImageConverter> List() { 
     return this.converters.ToList(); 
    } 

    public IList<IImageConverter> FindBySource(string extension) { 
     IEnumerable<IImageConverter> converters = this.converters 
      .Where(x => 
       x.SourceFileExtensions.Contains(extension)); 

     return converters.ToList(); 
    } 

    public IList<IImageConverter> FindByTarget(string extension) { 
     IEnumerable<IImageConverter> converters = this.converters 
      .Where(x => 
       x.TargetFileExtensions.Contains(extension)); 

     return converters.ToList(); 
    } 

    public IList<IImageConverter> Find(
     string sourceFileExtension, 
     string targetFileExtension) 
    { 
     IEnumerable<IImageConverter> converter = this.converters 
      .Where(x => 
       x.SourceFileExtensions.Contains(sourceFileExtension) && 
       x.TargetFileExtensions.Contains(targetFileExtension)); 

     return converter.ToList(); 
    } 

    public IImageConverter Find(string name) { 
     IEnumerable<IImageConverter> converter = this.converters 
      .Where(x => x.Name == name); 

     return converter.SingleOrDefault(); 
    } 
} 

的直接依賴您註冊在容器IPluginManager和簡單的注射器沒有休息:

container.Register<IPluginManager, PluginManager>(); 

我希望這有助於。

+0

謝謝。不過,我不太瞭解裝飾器的用途以及它們的用途。你能詳細解釋一下嗎? – tjeuten

+2

@tjeuten裝飾器是面向方面編程的一種形式 - 一種將交叉關注點添加到同一抽象(例如接口)的多個實現中的技術。在這個例子中,我使用了裝飾器爲所有插件添加通用代碼。圖像轉換器管理器是數據驅動的,它是否稱爲從'tif'轉換爲'bmp'或從'jpg'轉換爲'gif'的插件,管理器總是需要逐個圖像地更新數據庫。 – qujck

+0

我實際上會指向你一篇由@Steven撰寫的文章,他是Simple Injector的創建者,他也給出了這個問題的答案(善良知道爲什麼它被低估了!) - 文章可以找到[這裏](http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91) - 它引入了我的裝飾模式,並可能對你有所幫助。 – qujck

1

你需要探測插件目錄在啓動和使用.NET反射API從動態加載組件獲得庫類型。有很多方法可以做到這一點,這取決於你確切需要什麼。 Simple Injector中沒有API,只是因爲有很多方法可以做到這一點,編寫一些自定義的LINQ語句通常更具可讀性和靈活性。

這裏有一個如何這可能外觀的示例:

// Find all repository abstractions 
var repositoryAbstractions = (
    from type in typeof(ICustomerRepository).Assembly.GetExportedTypes() 
    where type.IsInterface 
    where type.Name.EndsWith("Repository") 
    select type) 
    .ToArray(); 

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

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

// Find all repository abstractions 
var repositoryImplementationTypes = 
    from assembly in pluginAssemblies 
    from type in assembly.GetExportedTypes() 
    where repositoryAbstractions.Any(r => r.IsAssignableFrom(type)) 
    where !type.IsAbstract 
    where !type.IsGenericTypeDefinition 
    select type; 

// Register all found repositories. 
foreach (var type in repositoryImplementationTypes) 
{ 
    var abstraction = repositoryAbstractions.Single(r => r.IsAssignableFrom(type)); 
    container.Register(abstraction, type); 
} 

上面的代碼是從簡單的噴油器文檔中的Registering plugins dynamically wiki page代碼示例的變化。

2

什麼你可能尋找這就是:

public class TypeLoader<T> : List<T> 
{ 
    public const BindingFlags ConstructorSearch = 
     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | 
     BindingFlags.Instance; 

    private void Load(params Assembly[] assemblies) 
    { 
     foreach (
      Type t in 
       assemblies.SelectMany(
        asm => 
         asm.GetTypes() 
          .Where(t => t.IsSubclassOf(typeof (T)) || t.GetInterfaces().Any(i => i == typeof (T))))) 
     { 
      Add((T) Activator.CreateInstance(t, true)); 
     } 
    } 
} 

所有你需要做的就是調用Assembly.ReflectionOnlyLoad從您的插件目錄中的組件,並將其傳遞給該方法。

如果,例如在你的組件中聲明你的所有插件類IPlugin,你會使用它像new TypeLoader<IPlugin>().Load(assemblies);,你會用你的對象實現IPlugin整齊的列表結束。

相關問題