如何使用 C# 代码覆盖 config.json 数据?

How to override config.json data using C# code?

我关注了这个关于如何将 IOC 与 Autofac 结合使用的博客 here,这是第一次听说 IOC 和 autoFac。

我已经从 link 提供的博客下载了该项目,我一直在浏览该项目,我正试图找出 类:

public class DatabaseSettings
{
    public string ConnectionString { get; protected set; }
    public int TimeoutSeconds { get; protected set; }
}

public class UserSettings
{
    public string DefaultUsername { get; protected set; }
    public bool ActiveByDefault { get; protected set; }
}

... 无需调用 'Database reader'?

中的加载函数即可填充

是不是因为(这些):

public T Load<T>() where T : class, new() => Load(typeof(T)) as T;

public T LoadSection<T>() where T : class, new() => LoadSection(typeof(T)) as T;

如果是上面的代码,它们是什么(这样我就可以了解它们是如何工作的)?

最后一个问题,是否可以使用这种方法将数据保存回 config.json?

这样的条目
public T Load<T>() where T : class, new() => Load(typeof(T)) as T;

只是意味着您可以在函数中访问时使用 "generic" 语法。这比将 Type 作为方法参数传递要简洁一些,并且还意味着您将返回一个 strongly-typed 对象。上面的另一种写法是:

public T Load<T>() where T : class, new()
{
    var type = typeof(T);
    var loaded = Load(type);
    return loaded as T;
}

这是一个有用的语言特性,但与 IoC 本身无关。 IoC 魔法本身主要包含在 SettingsModule 中。这个位:

builder.RegisterInstance(new SettingsReader(_configurationFilePath, _sectionNameSuffix))
    .As<ISettingsReader>()
    .SingleInstance();

告诉 Autofac 在任何人请求 ISettingsReader(As<> 位)时提供一个 SettingsReader(RegisterInstance 部分)。 .SingleInstance 意味着它将 SettingsReader 视为单例:只会创建其中一个,并将同一对象传递到请求 ISettingsReader 的任何地方。

这一部分

var settings = Assembly.GetExecutingAssembly()
    .GetTypes()
    .Where(t => t.Name.EndsWith(_sectionNameSuffix, StringComparison.InvariantCulture))
    .ToList();

settings.ForEach(type =>
{
    builder.Register(c => c.Resolve<ISettingsReader>().LoadSection(type))
        .As(type)
        .SingleInstance();
});

只是一种奇特的方式,当它看到对 DatabaseSettings 或 UserSettings 的请求时,自动告诉它该做什么。根据最初的问题,this 是实际调用 Load 函数的地方。一个更简单的方法是:

builder.Register(c => c.Resolve<ISettingsReader>().LoadSection(typeof(DatabaseSettings))).As<DatabaseSettings>();
builder.Register(c => c.Resolve<ISettingsReader>().LoadSection(typeof(UserSettings))).As<UserSettings>();

您可以将这些逻辑写成 "when a DatabaseSettings object is requested (.As), find an implementation for ISettingsReader, and then call LoadSection on that (the first part)"

容器中的其他地方 class 还有这个:

builder.RegisterType<UserService>().As<IUserService>();

这只是告诉 Autofac 为 IUserService 做什么。

结果是我们在主要应用方法中的位置:

using (var scope = container.BeginLifetimeScope())
{
    var userService = scope.Resolve<IUserService>();

如果没有 main 方法 "knowing" 关于它使用的具体类型的任何信息,我们将得到一个功能齐全的 IUserService。在内部,Autofac 将通过插入链中每种类型的所有构造函数参数来解析所需的依赖链。这可能看起来像:

  • 已请求 IUserService
    • 解析用户服务
      • 解析 IDatabase
        • return 数据库
      • 解析用户设置
        • 解析 ISettingsReader
          • return 设置阅读器
        • 在 ISettingsReader 上调用 LoadSection
        • return 生成了 UserSettings 对象

关于你的最后一个问题 - 是的!但是,IoC 不一定能让您这样做。它只是让您绑定在一起并访问您创建的任何自定义 classes 以允许保存。

您可以像这样创建一个新界面

public interface ISettingsWriter
{
    void Save<T>(T settings);
}

然后出于某种原因,您添加了一个方法来访问 UserService 中的方法:

public class UserService : IUserService
{
    private readonly IDatabase _database;
    private readonly UserSettings _userSettings;
    private readonly ISettingsWriter _settingsWriter;

    public UserService(IDatabase database, UserSettings userSettings, ISettingsWriter settingsWriter)
    {
        _database = database;
        _userSettings = userSettings;
        _settingsWriter = settingsWriter;
    }

    public void UpdateUserSettings()
    {
        _settingsWriter.Save(new UserSettings());
    }

以这种方式使用它比在原始示例代码中更简单一些 - 我建议您采用这种方法,直到您更加习惯为止。这意味着您唯一需要添加的另一件事是设置编写器的注册,例如:

builder.RegisterType<SettingsWriter>()
    .As<ISettingsWriter>();