2015-01-10 3 views
0

나는 의존성 주입을 Ninject와 함께 사용하는 법을 배우고 있으며 System.IO.Abstractions을 사용하여 파일 시스템을 추상화하고 있습니다. '유형의 매개 변수로 사용할 수 없습니다System.IO.Abstractions를 Ninject와 바인딩 할 수없는 이유는 무엇입니까?

IKernel ninject = new StandardKernel(); 
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfo>(); 

을하지만 오류

오류 1 유형'System.IO.DirectoryInfo '을 얻고있다 : 난 너무로 DirectoryInfoDirectoryInfoBase를 바인딩해서 Ninject를 사용하는 것을 시도하고있다 TImplementation '제네릭 형식 또는 메서드'Ninject.Syntax.IBindingToSyntax.To() '. 'System.IO.DirectoryInfo'에서 'System.IO.Abstractions.DirectoryInfoBase'로의 암시적인 참조 변환은 없습니다. C : 사용자 \ \ 트레버 \ 드롭 박스 \ 코드 \ PhotoOrganiser \ PhotoOrganiser \ Program.cs 13 13 PhotoOrganiserApp

내가 여기서 무엇을 놓치고? 나는 이런 종류의 일들을 수행 할 수있는 것이 도서관의 목표라고 생각했다.

답변

4

System.IO.Abstractions는 Adapter Pattern을 사용하고 있습니다. DI와 함께 사용하기 위해 추상화 (추상 클래스 또는 인터페이스)가없는 특정 유형에 사용되는 트릭입니다. .NET에서 기존 형식에 추상화를 추가 할 방법이 없으므로 구현을 느슨하게 결합하는 데 사용하기 위해 추상화 (이 경우 추상 클래스)가있는 래퍼 (어댑터)가 만들어집니다.

여기서 문제는 래퍼를 사용하지 않고 직접 구현을 사용하고 있다는 것입니다.

IKernel ninject = new StandardKernel(); 
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfoWrapper>() 
    .WithConstructorArgument("instance", new DirectoryInfo(@"C:\Somewhere\")); 

그러나 여기에는 또 다른 문제가 있습니다. DirectoryInfo에는 생성자 인수로 디렉토리 경로가 필요합니다. 따라서 이것은 일반적으로 Abstract Factory을 사용하는 것이 더 합리적임을 의미하므로 디렉토리 경로를 알고있을 때 런타임에 만들 수 있습니다. 이 경우 팩토리를 서비스에 삽입 한 다음 런타임에 메소드를 호출하여 인스턴스를 생성하는 것이 더 합리적입니다. System.IO.Abstractions의 저자는 공장을 내부적으로 만들었지 만, 같은 것을 만들 수는 있습니다.

[Serializable] 
public class DirectoryInfoFactory : IDirectoryInfoFactory 
{ 
    public DirectoryInfoBase FromDirectoryName(string directoryName) 
    { 
     var realDirectoryInfo = new DirectoryInfo(directoryName); 
     return new DirectoryInfoWrapper(realDirectoryInfo); 
    } 
} 

public class SomeService : ISomeService 
{ 
    private readonly IDirectoryInfoFactory directoryInfoFactory; 

    public SomeService(IDirectoryInfoFactory directoryInfoFactory) 
    { 
     if (directoryInfoFactory == null) 
      throw new ArgumentNullException("directoryInfoFactory"); 
     this.directoryInfoFactory = directoryInfoFactory; 
    } 

    public void DoSomething() 
    { 
     // The directory can be determined at runtime. 
     // It could, for example, be provided by another service. 
     string directory = @"C:\SomeWhere\"; 

     // Create an instance of the DirectoryInfoWrapper concrete type. 
     DirectoryInfoBase directoryInfo = this.directoryInfoFactory.FromDirectoryName(directory); 

     // Do something with the directory (it has the exact same interface as 
     // System.IO.DirectoryInfo). 
     var files = directoryInfo.GetFiles(); 
    } 
} 

그런 다음 단일 인스턴스가 아닌 여러 런타임 인스턴스를 생성 할 수있는 팩토리를 주입하도록 컨테이너를 구성하십시오.

IKernel ninject = new StandardKernel(); 
ninject.Bind<IDirectoryInfoFactory>().To<DirectoryInfoFactory>(); 

그러나 System.IO.Abstractions의 제작자가 한 걸음 더 나아가는 데 사용 된 트릭이 있습니다. 그는 삽입 할 수있는 Aggregate Service을 만들었으며 System.IO 네임 스페이스의 유형이 느슨하게 결합 된 방식으로 제공되는 많은 서비스를 제공합니다.

그래서 자신의 공장을 만드는 대신 System.IO 네임 스페이스가 제공하는 거의 모든 서비스에 대한 액세스 권한을 얻기 위해 기존의 IFileSystem 서비스를 삽입 할 수 있습니다.

public class SomeService : ISomeService 
{ 
    private readonly IFileSystem fileSystem; 

    public SomeService(IFileSystem fileSystem) 
    { 
     if (fileSystem == null) 
      throw new ArgumentNullException("fileSystem"); 
     this.fileSystem = fileSystem; 
    } 

    public void DoSomething() 
    { 
     // The directory can be determined at runtime. 
     // It could, for example, be provided by another service. 
     string directory = @"C:\SomeWhere\"; 

     // Create an instance of the DirectoryInfoWrapper concrete type. 
     DirectoryInfoBase directoryInfo = this.fileSystem.DirectoryInfo.FromDirectoryName(directory); 

     // Do something with the directory (it has the exact same interface as 
     // System.IO.DirectoryInfo). 
     var files = directoryInfo.GetFiles(); 
    } 
} 

그런 다음 IFileSystem을 주입하여 System.IO의 모든 기능을 얻을 수 있도록 컨테이너를 구성합니다.

IKernel ninject = new StandardKernel(); 
ninject.Bind<IFileSystem>().To<FileSystem>(); 
0

DirectoryInfoDirectoryInfoBase을 상속하거나 구현하지 않으므로 해당 바인딩을 사용할 수 없습니다.

당신은

DirectoryInfo dirInfo; 
DirectoryInfoBase dirInfoBase = (DirectoryInfoBase)dirInfo; 

를 사용하여 DirectoryInfoBaseDirectoryInfo 캐스팅 할 수있는 사실에 의해 오도 된 수도 있지만 그 때문에 DirectoryInfoBase에서 암시 적 캐스트 연산자 만 가능

:

public static implicit operator DirectoryInfoBase(DirectoryInfo directoryInfo) 
{ 
    if (directoryInfo == null) 
     return null; 
    return new DirectoryInfoWrapper(directoryInfo); 
} 

저는 System.IO.Abstractions에 익숙하지 않지만 exa와 같이 IFileSystem을 삽입하십시오.

ninject.Bind<IFileSystem>().To<FileSystem>(); 

와 mple은 당신이 가지고있는 경우 IFileSystem 당신은

fileSystem.DirectoryInfo.FromDirectoryName(directoryName) 

DirectoryInfoBase 개체를 가져 할 수 있습니다.

관련 문제