C# 控制/依赖注入的文件系统和反转

C# 控制/依赖注入的文件系统和反转,c#,dependency-injection,asp.net-core,C#,Dependency Injection,Asp.net Core,在控制器中,允许我们从文件系统读取文件内容,但不允许我们访问appsettings.json中的值。我理解将代码与特定于系统的实现细节分离的价值,但这是否意味着只应通过注入访问文件依赖关系?我希望了解IoC/DI的最佳实践 相比之下,在本例中,如果我将收件人的电子邮件地址保存在一个文件中而不是appsettings中,该怎么办?(我不是说这是个好主意) string body=string.Empty; //允许 字符串filePath=Path.GetFullPath(“Templates/T

在控制器中,允许我们从文件系统读取文件内容,但不允许我们访问
appsettings.json
中的值。我理解将代码与特定于系统的实现细节分离的价值,但这是否意味着只应通过注入访问文件依赖关系?我希望了解IoC/DI的最佳实践

相比之下,在本例中,如果我将收件人的电子邮件地址保存在一个文件中而不是appsettings中,该怎么办?(我不是说这是个好主意)

string body=string.Empty;
//允许
字符串filePath=Path.GetFullPath(“Templates/ToEnrollee.html”);
使用(StreamReader=新StreamReader(文件路径))
{
body=reader.ReadToEnd();
}
//不准
string recipient=Configuration.GetValue(“收件人”);

从应用程序的服务中读取文件是运行时行为。服务具有运行时行为是正常的

但是,读取配置设置就是配置。这通常在应用程序启动时执行一次,以便定制组合应用程序的特定设置

更重要的是,直接从服务读取配置设置会将服务直接耦合到配置文件。这是主要的问题-没有办法交换这些设置进行测试,或者如果您出于某种原因需要覆盖它们(相反,您可以在应用程序启动时放置一些简单的逻辑来覆盖文件中的任何设置)


另一方面,如果您的服务从一个文件中读取数据,那么只有在您没有向该文件传递路径或开放流时,它才会紧密耦合。允许传入路径意味着您可以测试该服务,而无需耦合到文件系统上的特定文件。如果您可以传入一个流(请记住,
stream
是一个抽象类),以便在不直接支持
FileStream
的情况下,它可以与
MemoryStream
或其他类型的流进行交换,这会更好。当然,将其他业务逻辑与读/写文件的代码分离也很重要,这样就可以在完全不依赖文件系统的情况下测试业务逻辑。

我永远不会直接使用内置的文件系统类(路径、文件、目录等)。我总是想把它们抽象出来

一个非常简单的例子可能是:

public interface IFileSystem
{
    string ReadAllText(string path);

    //... other file system operations
}

public class WindowsFileSystem : IFileSystem
{
    public string ReadAllText(string path)
    {
        return File.ReadAllText(path);
    }
}
在定义依赖项时,可以将其替换为适合应用程序运行环境的实现


这将允许您注入实现,以完全控制文件系统的使用,并帮助进行单元测试。

“…为什么可以…”谁告诉您可以?您将如何注入文件?爆笑。您使用DI作为依赖项,一个文件以及。。。没有依赖性。选项可以加载到模型中并注入,而文件不能很好地从文件中读取,具体取决于实现。您正在使用StreamReader。如果您想遵循IoC原则,您将依赖于抽象,而不是实现。幸运的是,阅读文件时,您可以使用该库。“如果我将收件人的电子邮件地址保存在一个文件而不是appsettings中会怎么样?”——除了
appsettings.json
显然也是一个文件之外,它没有任何问题。只是您需要反复读取该文件,或者创建一些缓存机制,您还需要在其中加入依赖项注入,以便访问它。然后,您可能希望允许不同的文件格式和多个源,并支持在不重新启动的情况下更改文件…突然,您自己实现了
Microsoft.Extensions.Configuration
。对于任何IO,我都会这么说。读取配置与读取文件相同。如果一个应用程序决定在Azure上运行,而你想将所有系统IO替换为Azure文件存储,那么你就完蛋了,除非你还抽象了系统IO。我同意这种方法,但它是抽象依赖项,而不是注入依赖项。也许它措词不当,但是我的意思是你可以注入你的抽象实现。为什么要重新发明轮子呢?有一个为你提供接口的图书馆。因为我发现最好的学习方法是自己尝试这些东西,而不是总是依赖于找到一个为你做了工作却不真正了解他们在做什么的图书馆。。。而且。。。编写代码比集成其他人的库有趣得多;)学习并了解引擎盖下的工作原理是很好的。但是,当实际需要在生产环境中完成某项工作时,花时间编写自己的库,而不是使用经过战斗测试的库,如果出现错误,可以由团队之外的人修复,这是荒谬的。
public interface IFileSystem
{
    string ReadAllText(string path);

    //... other file system operations
}

public class WindowsFileSystem : IFileSystem
{
    public string ReadAllText(string path)
    {
        return File.ReadAllText(path);
    }
}