【问题标题】:Ninject Constructor argument check parameter existenceNinject Constructor参数检查参数是否存在
【发布时间】:2018-02-08 06:30:39
【问题描述】:

我想知道是否有,如何使用 Ninject 进行参数存在检查?

我指的是:让我们有一个理论类和一个接口:

public interface IFileExistenceCheck
{
    bool FileExist();
}

public class FileExistenceChecker : IFileExistenceCheck
{
    private readonly string filePath;
    private readonly IFileSystem fileSystem;

    public FileExistenceChecker(IFileSystem fileSystem, string filePath)
    {
        this.fileSystem = fileSystem;
        this.filePath = filePath;
    }

    public bool FileExist()
    {
        return this.fileSystem.File.Exists(this.filePath);
    }
}

然后在代码中的某处,我将通过内核获取IFIleExistenceCheck 接口的实例,如下所示:

public class Foo()
{
    public void Bar()
    {
        // do something
        var filePath = SomeMagicString;

        var filePathArgument = new ConstructorArgument("filePath", filePath); // <- This part I do not really like
        var checker = Kernel.Get<IFileExistenceCheck>(filePathArgument);
        var fileExist = checker.FileExist();

        // do the rest of code
    }
}

这可以正常工作,问题是,只要文件路径参数的名称保持不变,它就可以工作。假设有一天有人会决定,filePath 是不必要的,并将其重命名为 path。代码本身仍会编译,但在有人真正调用Bar() 方法之前不会导致任何错误。

有什么办法可以防止这种情况发生吗?

我真的不想暴露filePath。我仍然希望它作为构造函数的参数传递。我不想将FileCheck() 的签名更改为接受filePath 作为参数,我什至不想将filePath 更改为可公开访问的字段。

【问题讨论】:

  • filePath 在启动时是否已知?还是运行时数据?

标签: c# .net dependency-injection inversion-of-control ninject


【解决方案1】:

这是在滥用 DI 容器作为 service locator还有一个原因为什么它的服务定位器被认为是 anti-pattern

如果您使用依赖注入模式,您的人为示例将不会发生。重构您的示例以使用依赖注入,它看起来像这样:

public interface IFileExistanceCheck
{
    bool FileExist(string filePath);
}

public class FileExistanceChecker : IFileExistanceCheck
{
    private readonly IFileSystem fileSystem;

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

    // Pass runtime data through the method parameters!
    public bool FileExist(string filePath)
    {
        // Prevent an empty file path from being used
        if (string.IsNullOrEmpty(filePath))
            throw new ArgumentNullException(nameof(filePath));

        return this.fileSystem.File.Exists(filePath);
    }
}

Foo 类

public class Foo
{
    private readonly IFileExistanceCheck fileExistanceCheck;

    public Foo(IFileExistanceCheck fileExistanceCheck)
    {
        if (fileExistanceCheck == null)
            throw new ArgumentNullException(nameof(fileExistanceCheck));
        this.fileExistanceCheck = fileExistanceCheck;
    }
    public void Bar()
    {
        // do something
        var filePath = SomeMagicString;

        var fileExist = fileExistanceCheck.FileExist(filePath);

        // do the rest of code
    }
}

composition root,Ninject 会将它们捆绑在一起并让Foo 运行。

class Program
{
    static void Main(string[] args)
    {
        // Begin composition root

        var kernel = new StandardKernel();

        kernel.Bind<IFileSystem>().To<FileSystem>();
        kernel.Bind<IFileExistanceCheck>().To<FileExistanceChecker>();

        var app = kernel.Get<Foo>();

        // End composition root

        app.Bar();
    }
}

如果需要检查filePath 参数是否存在,可以使用保护子句来完成此检查。您只需要使用dependency injection pattern,并通过方法参数传递运行时数据(文件路径)。

如果您希望filePath 成为在应用程序启动时通过构造函数传递的配置值,那么使用服务来检查文件是否存在似乎毫无意义。在这种情况下,您应该在允许您的应用程序运行之前检查该文件是否存在。

class Program
{
    static void Main(string[] args)
    {
        var filePath = "SomeFileThatShouldExist.txt";

        // Check configuration
        if (!File.Exists(filePath))
            throw new InvalidOperationException("Invalid configuration");

        // Begin composition root

        var kernel = new StandardKernel();

        kernel.Bind<Foo>().To(new Bar(filePath));
        // Register other services...

        var app = kernel.Get<Foo>();

        // End composition root

        app.Bar();
    }
}

【讨论】:

    【解决方案2】:

    如果您的参数仅在运行时已知,您可以注入工厂并使用它创建您的FileExistenceChecker

    public interface IFileExistenceCheckerFactory
    {
        IFileExistenceCheck Create(string path);
    }
    
    ...
    
    var kernel = new StandardKernel();
    kernel.Bind<IFileExistenceCheck>().To<FileExistenceChecker>();
    kernel.Bind<IFileSystem>().To<FileSystem>();
    kernel.Bind<IFileExistenceCheckerFactory>().ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
    
    var factory = kernel.Get<IFileExistenceCheckerFactory>();
    var checker = factory.Create("SomeMagicString");
    var fileExist = checker.FileExist();
    

    那么即使你的参数名称不匹配,TypeMatchingArgumentInheritanceInstanceProvider 也要确保参数将与其类型匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-22
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多