Ninject构造函数参数检查参数存在

时间:2018-02-08 06:30:39

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

我想知道是否还有,如何使用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更改为可公开访问的字段。

2 个答案:

答案 0 :(得分:5)

这会将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 Class

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参数,可以使用guard子句进行此检查。您只需要使用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();
    }
}

答案 1 :(得分:1)

如果您的参数仅在运行时知道,您可以注入工厂并使用它创建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也要确保参数与其类型匹配。