为什么我不能将System.IO.Abstractions与Ninject绑定?

tsv*_*der 0 .net c# dependency-injection ninject

我只是学习使用Ninject依赖注入,并使用System.IO.Abstractions来抽象文件系统.我试图使用Ninject绑定DirectoryInfoBaseDirectoryInfo如此:

IKernel ninject = new StandardKernel();
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfo>();
Run Code Online (Sandbox Code Playgroud)

但我得到了错误

错误1类型'System.IO.DirectoryInfo'不能用作泛型类型或方法'Ninject.Syntax.IBindingToSyntax.To()'中的类型参数'TImplementation'.没有从'System.IO.DirectoryInfo'到'System.IO.Abstractions.DirectoryInfoBase'的隐式引用转换.C:\ Users\Trevor\Dropbox\Code\PhotoOrganiser\PhotoOrganiser\Program.cs 13 13 PhotoOrganiserApp

我在这里错过了什么?我认为像这样的图书馆的目标是能够执行这些类型的任务吗?

Nig*_*888 5

System.IO.Abstractions正在使用适配器模式.它是一种技巧,用于某些类型,没有任何抽象(抽象类或接口),以便与DI一起使用它们.由于无法在.NET中为现有类型添加抽象,因此创建了一个包装器(适配器),它具有抽象(在本例中为抽象类),以便用于松散地耦合实现.

这里的问题是你没有使用包装器,你直接使用实现.

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

但是,这里还有另一个问题 - DirectoryInfo需要一个目录路径作为构造函数参数.因此,这意味着使用抽象工厂通常更有意义,因此可以在目录路径已知时在运行时创建它.在这种情况下,将工厂注入服务然后调用方法在运行时创建实例更有意义.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后配置容器以注入可以创建多个运行时实例而不是单个实例的工厂.

IKernel ninject = new StandardKernel();
ninject.Bind<IDirectoryInfoFactory>().To<DirectoryInfoFactory>();
Run Code Online (Sandbox Code Playgroud)

但是System.IO.Abstractions的作者还有另一个技巧,它将它更进了一步.他创建了一个可以注入的聚合服务,并以松散耦合的方式提供System.IO命名空间中提供的许多类型的服务.

因此,您可以改为注入现有的IFileSystem服务,而不是创建自己的工厂,以便访问System.IO命名空间提供的几乎任何服务.

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将配置容器只是为了注入IFileSystem以获得System.IO的所有功能.

IKernel ninject = new StandardKernel();
ninject.Bind<IFileSystem>().To<FileSystem>();
Run Code Online (Sandbox Code Playgroud)