简体   繁体   English

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

[英]Why can't I bind System.IO.Abstractions with Ninject?

I am just learning to use dependency injection with Ninject, and am using System.IO.Abstractions to abstract the filesystem. 我只是在学习将依赖注入与Ninject一起使用,并且正在使用System.IO.Abstractions来抽象文件系统。 I am trying to use Ninject to bind DirectoryInfoBase to DirectoryInfo as so: 我正在尝试使用Ninject将DirectoryInfoBase绑定到DirectoryInfo如下所示:

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

But am getting the error 但是我得到了错误

Error 1 The type 'System.IO.DirectoryInfo' cannot be used as type parameter 'TImplementation' in the generic type or method 'Ninject.Syntax.IBindingToSyntax.To()'. 错误1类型'System.IO.DirectoryInfo'不能用作通用类型或方法'Ninject.Syntax.IBindingToSyntax.To()'中的类型参数'TImplementation'。 There is no implicit reference conversion from 'System.IO.DirectoryInfo' to 'System.IO.Abstractions.DirectoryInfoBase'. 从“ System.IO.DirectoryInfo”到“ System.IO.Abstractions.DirectoryInfoBase”没有隐式引用转换。 C:\\Users\\Trevor\\Dropbox\\Code\\PhotoOrganiser\\PhotoOrganiser\\Program.cs 13 13 PhotoOrganiserApp C:\\ Users \\ Trevor \\ Dropbox \\ Code \\ PhotoOrganiser \\ PhotoOrganiser \\ Program.cs 13 13 PhotoOrganiserApp

What am I missing here? 我在这里想念什么? I thought the goal of libraries such as this was to be able to perform these sorts of tasks? 我认为诸如此类的库的目标是能够执行此类任务?

System.IO.Abstractions is using the Adapter Pattern . System.IO.Abstractions正在使用适配器模式 It is a trick used for certain types that don't have any abstraction (abstract class or interface) in order to use them with DI. 对于某些没有任何抽象的类型(抽象类或接口)以便与DI一起使用,这是一个技巧。 Since there is no way to add an abstraction to an existing type in .NET, a wrapper (adapter) is created which has an abstraction (in this case, an abstract class) in order to use to loosely couple the implementation. 由于无法将抽象添加到.NET中的现有类型,因此将创建具有抽象(在本例中为抽象类)的包装器(适配器),以用于松散耦合实现。

The problem here is that you are not using the wrapper, you are using the implementation directly. 这里的问题是您没有使用包装器,而是直接使用实现。

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

However, there is another gotcha here - DirectoryInfo requires a directory path as a constructor argument. 但是,这里还有另一个陷阱-DirectoryInfo需要目录路径作为构造函数参数。 So this means it typically makes more sense to use an Abstract Factory so it can be created at runtime when the directory path is known. 因此,这意味着使用抽象工厂通常更有意义,这样可以在运行时在知道目录路径的情况下创建它。 In this case, it makes more sense to inject the factory into your service and then call the method to create the instance at runtime. 在这种情况下,将工厂注入服务中,然后在运行时调用该方法来创建实例更有意义。 The author of System.IO.Abstractions made the factory internal, but you can build one just the same. 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();
    }
}

And then configure the container to inject a factory that can create multiple runtime instances rather than a single instance. 然后配置容器以注入可以创建多个运行时实例而不是单个实例的工厂。

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

But there is another trick the author of System.IO.Abstractions used to take it a step further. 但是System.IO.Abstractions的作者还有另一个技巧,可以使它更进一步。 He made an Aggregate Service that can be injected and provide many of the services that types in the System.IO namespace provide in a loosely-coupled way. 他制作了一个聚合服务 ,可以将其注入并提供以松散耦合方式在System.IO名称空间中提供的许多服务。

So, rather than making your own factory, you could instead inject the existing IFileSystem service in order to gain access to virtually any of the services the System.IO namespace provides. 因此,您可以注入现有的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();
    }
}

You would then configure the container just to inject IFileSystem to gain all of the functionality of System.IO. 然后,您可以将容器配置为仅注入IFileSystem,以获得System.IO的所有功能。

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

You can't use that particular binding because DirectoryInfo doesn't inherit from or implement DirectoryInfoBase . 您不能使用该特定绑定,因为DirectoryInfo不会继承或实现DirectoryInfoBase

You might have been misled by the fact you can do cast a DirectoryInfo to a DirectoryInfoBase using 您可能会误以为可以使用以下方法将DirectoryInfo转换为DirectoryInfoBase

DirectoryInfo dirInfo;
DirectoryInfoBase dirInfoBase = (DirectoryInfoBase)dirInfo;

but that is only possible because of an implicit cast operator in DirectoryInfoBase : 但这仅是可能的,因为DirectoryInfoBase有一个隐式强制转换运算符:

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

I'm not familiar with System.IO.Abstractions but why don't you just inject the IFileSystem , like in the example with 我对System.IO.Abstractions不熟悉,但是为什么不像下面的示例那样直接注入IFileSystem呢?

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

If you have a IFileSystem you can do 如果您有IFileSystem ,则可以执行

fileSystem.DirectoryInfo.FromDirectoryName(directoryName)

to get a DirectoryInfoBase object. 获取DirectoryInfoBase对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM