【问题标题】:Why can't I bind System.IO.Abstractions with Ninject?为什么我不能将 System.IO.Abstractions 与 Ninject 绑定?
【发布时间】:2015-01-10 09:41:10
【问题描述】:

我刚刚学习在 Ninject 中使用依赖注入,并且正在使用 System.IO.Abstractions 来抽象文件系统。我正在尝试使用 Ninject 将DirectoryInfoBase 绑定到DirectoryInfo,如下所示:

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

但我得到了错误

错误 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

我在这里缺少什么?我认为像这样的库的目标是能够执行这些类型的任务?

【问题讨论】:

    标签: c# .net dependency-injection ninject


    【解决方案1】:

    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命名空间中的类型以松散耦合的方式提供的许多服务。

    因此,与其创建自己的工厂,不如注入现有的 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();
        }
    }
    

    然后您将配置容器以注入 IFileSystem 以获得 System.IO 的所有功能。

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

    【讨论】:

      【解决方案2】:

      您不能使用该特定绑定,因为 DirectoryInfo 不继承或实现 DirectoryInfoBase。

      您可能被以下事实误导了:您可以将 DirectoryInfo 转换为 DirectoryInfoBase 使用

      DirectoryInfo dirInfo;
      DirectoryInfoBase dirInfoBase = (DirectoryInfoBase)dirInfo;
      

      但这只能通过DirectoryInfoBase 中的隐式转换运算符来实现:

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

      我不熟悉System.IO.Abstractions,但你为什么不直接注入IFileSystem,就像在示例中一样

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

      如果你有IFileSystem,你可以这样做

      fileSystem.DirectoryInfo.FromDirectoryName(directoryName)
      

      获取DirectoryInfoBase 对象。

      【讨论】:

        猜你喜欢
        • 2018-01-28
        • 1970-01-01
        • 2012-01-17
        • 2012-07-16
        • 2010-10-04
        • 2013-05-29
        • 2013-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多