2014-03-24 7 views
4

다른 구현 (예 : 다른 고객에게 구현)되어있는 .NET 4.5 시스템을 구축 중입니다. 각 고객은 고유 한 인프라와 데이터베이스 구조를 가지므로 양파 아키텍처에 크게 의존하는 시스템을 구축하고 있으며 인터페이스와 DI에 의존합니다. 그렇게하면 고객 별 "리포지토리"및 "서비스"구현을 사용할 수 있습니다.간단한 인젝터 플러그인

내 목표는 다시 컴파일하지 않고 고객 서버에 시스템을 설치할 수있게하는 것입니다 (시스템 진입 점은 기본적으로 정기적으로 발생하는 비즈니스 논리를 포함하고 WCF 서비스를 호스팅하는 Windows 서비스입니다). 그게 작동하도록, 내가 염두에두고있는 일은 필요한 모든 인터페이스를 구현하는 구체적인 클래스를 가진 고객 전용 DLL을 포함하는 Windows 서비스 실행 파일이 들어있는 폴더의 하위 폴더와 같은 일종의 "Dependencies"또는 "Plugins"폴더입니다 응용 프로그램에 의존합니다.

나는 이것을 Simple Injector로 시도하고있다. 나는 SimpleInjector.Packaging 어셈블리와 "플러그인 동적 등록"에 대한 단락을 살펴 봤지만, 여전히 붙어 다니기가 쉬우 며 어디서부터 시작해야할지 모르겠다. 어떤 어셈블리에서 정의해야하는지 .

저는 이것을 달성하는 방법에 대한 구체적인 샘플이 필요합니다.

SimpleInjector Packaging 어셈블리가이 용도로 사용됩니까? 아니면 잘못 생각합니까? 그렇다면 어떻게?

아무도 저에게 계몽하십시오.

감사

추신 : 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

Simple Injector와 같은 IoC 컨테이너와 함께이 작업을 수행하는 것이 모든 플러그인에 공통 논리를 추가하는 것이 매우 쉽다는 점이 장점입니다. 필자는 최근 새로운 이미지 변환기를 연결하는 대량 이미지 변환기 유틸리티를 작성했습니다.

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

등록은 다음과 같이 이루어집니다 인터페이스 모든 플러그인에 필요한

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; 
} 

공통 로직 (.NET 어셈블리없는 플러그인 종속성 검사 오류 주)입니다 작업이 장식 자에 의해 처리됩니다.

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 유타 나는 당신은 용기에 IPluginManager을 등록 컨테이너

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

에 직접 종속성을 복용하지 않고 당신이 필요한 플러그인을 찾을 수있는 방법에 대한 예로 추가하고 간단한 인젝터 나머지 부분을 수행합니다

container.Register<IPluginManager, PluginManager>(); 

도움이 되었기를 바랍니다.

+0

감사합니다. 그러나 저는 데코레이터의 사용과 그 목적이 무엇인지에 대해서는 잘 모릅니다. 이걸 조금 더 자세히 설명해 주시겠습니까? – tjeuten

+2

@tjeuten Decorator는 Aspect Oriented Programming의 한 형태로, 동일한 추상화 (예 : 인터페이스)의 여러 구현에 교차 절단 문제를 추가하는 기술입니다. 이 예제에서는 데코레이터를 사용하여 모든 플러그인에 공통 코드를 추가했습니다. 이미지 변환기 관리자는 데이터 기반이며 관리자가 'tif'에서 'bmp'로 또는 'jpg'에서 'gif'로 변환하기 위해 플러그인을 호출하는지 여부에 관계없이 관리자는 항상 이미지 단위로 데이터베이스를 업데이트해야합니다. – qujck

+0

사실 Simple Injector의 작성자 인 @Steven이 작성한 기사와이 질문에 대한 답을 주신 기사를 가리키게 될 것입니다 (선하심이 왜 투표에 실패했는지 알고 있습니다!). 기사를 찾을 수 있습니다. [here] (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를 구현하는 모든 오브젝트의 깔끔한 목록을하게 될 겁니다.

관련 문제