当前位置: 首页 > 知识库问答 >
问题:

C#.NET核心配置-替代方法?

太叔超英
2023-03-14

我正在寻找在.NET Core中混合使用环境无关、环境特定和秘密配置的选项模式的简单方法。我看到了不必要的复杂的方法,并开发了一种方法,它感觉比我所看到的要简单得多,但仍然需要进一步简化。我会在这个问题的第一个答案中提供这一点。

因此,明确地提出一个问题:将选项对象绑定到.NET Core中的配置节的最简单方法是什么,它在所有类型的.NET Core项目(例如函数应用程序、辅助服务、ASP.NET Core等)中都是一致的?

附加问题:如何在不需要使用依赖注入容器的情况下使用这种方法?

  • 创建具有依赖注入容器的IConfiguration实例对我来说非常麻烦,因为IConfiguration需要成为依赖关系链中每个构造函数的参数,或者需要在DI容器中注册所有类/接口实现。

这种方法如何与依赖注入容器一起使用?

  • 我遇到的大多数人都使用DI容器(Autofac或.NET Core DI容器),因此讨论该方法在DI容器场景中如何也有用是绝对值得的。

=============

值得注意的是,我通过依赖注入完全遵守依赖反转原则,但通过链接构造函数(通常是两个)来做到这一点,并且仅在.NET内核中最低限度地使用DI容器--几乎总是仅用于日志记录。这有很多原因,可能是Ygor Bugayenko在《优雅的对象》中阐述的最好的,而不是本文的主题。

=============

还有一件事:我不会接受我自己在下面提供的答案。事实上,我暂时不会接受一个答案,以便有足够的时间在答案中提供其他方法。

期待您的点子!干杯

共有1个答案

秦凯旋
2023-03-14

一种方法是创建延迟初始化的Options对象,并使用IConfiguration的实现,该实现在启动时存储在一个可供整个应用程序使用的singleton中,以消除对DI容器体操的需要。我相信下面的方法并没有偏离Microsoft关于使用选项模式的意图,而是一种不同的、更简单的方法。

在这个示例实现中,为了简单起见,我将使用朴素的单例模式--如果您关注多个线程创建多个实例,那么您可以使用Lazy类或传统的双锁单例模式。

我将使用RemoteCache配置对象作为Options兼容模型的示例:

public class RemoteCache 
{
    private const string SectionName = "Redis";
    private static RemoteCache _instance;
    private static readonly Redis RedisConfig = new();

    public static RemoteCache Instance() 
    {
        if (_instance is not null) return _instance;

        _instance = new RemoteCache();
        return _instance;
    }

    private RemoteCache() { }

    public string CacheConnection => RedisConfigInstance.CacheConnection;
    public string CacheKey => RedisConfigInstance.CacheKey;

    private Redis RedisConfigInstance
    {
        get
        {
            if (string.IsNullOrWhiteSpace(RedisConfig.CacheConnection) is false) return RedisConfig;

            AppConfiguration.Instance.GetSection(SectionName).Bind(RedisConfig);
            return RedisConfig;
        }
    }

    private class Redis
    {
        public string CacheConnection { get; set; } = string.Empty;
        public string CacheKey { get; set; } = string.Empty;
    }
}

Q.什么是AppConfiguration?它在哪里初始化?

A.在启动类中初始化的静态类。

我的一个函数应用程序的启动类通常是这样的。您可能会注意到,我使用的是“appsettings.json”,这在函数应用程序中通常不会这样做,但我希望我的配置中与环境无关的部分遵循在非函数应用程序中使用的相同模式,而不是将几乎所有的东西都塞进环境变量中,这似乎是函数应用程序的典型做法,并导致在部署过程中推入大量的环境变量。我确实在非本地部署中为机密使用环境变量,并在运行时使用KeyVault引用获取那些机密。在本地运行时,使用usersecrets获取机密。

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder) 
    {
        ExecutionContextOptions executionContextOptions = builder.Services.BuildServiceProvider().GetService<IOptions<ExecutionContextOptions>>().Value;

        IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
            .SetBasePath(executionContextOptions.AppDirectory)
            .AddEnvironmentVariables()
            .AddJsonFile("appsettings.json", false)
            .AddJsonFile("local.settings.json", true)
            .AddUserSecrets(Assembly.GetExecutingAssembly(), true);

        AppConfiguration.Instance = configurationBuilder.Build();

        builder.Services.AddLogging(logging =>
        {
            logging.AddApplicationInsights(new ApplicationInsightsConfiguration().AppInsightsInstrumentationKey());
        }
    }
}

AppConfiguration类的全部内容是:

public static class AppConfiguration
{
    public static IConfiguration Instance;
}

因此,一旦您有了启动代码和AppConfiguration类,您就可以愉快地创建Options model对象,这些对象可以随心所欲地映射到配置部分。这些模型被实现为单例,因此您可以从需要配置的任何代码访问它们,无论它们在应用程序中嵌套得有多深。

用法示例:

(我更喜欢通过一个接口来访问配置模型,它允许我的代码与使用配置实现的赝品的真正单元测试一起使用TDD(当然,如果您愿意,可以使用模拟))

public interface IRemoteCacheConfiguration
{
    string CacheConnection();
    string RedisCacheKey();
}

public class RemoteCacheConfiguration : IRemoteCacheConfiguration
{
    public string CacheConnection() => RemoteCache.Instance().CacheConnection;
    public string RedisCacheKey() => RemoteCache.Instance().CacheKey;
}

public class MyThingThatAccessesTheCache
{
    private readonly IRemoteCacheConfiguration _remoteCacheConfiguration;

    public MyThingThatAccessesTheCache() : this(new RemoteCacheConfiguration()) { }

    public MyThingThatAccessesTheCache(IRemoteCacheConfiguration remoteCacheConfiguration) => _remoteCacheConfiguration = remoteCacheConfiguration;

    public void DoStuff()
    {
        string cacheConnection = _remoteCacheConfiguration.CacheConnection();
    }
}
 类似资料:
  • .NET核心和ASP.NET核心到底有什么区别?

  • 遵循以下文档:https://docs.microsoft.com/fr-fr/aspnet/core/grpc/authn-and-authz?view=aspnetcore-3.0#承载令牌身份验证,我尝试配置我的GRPC客户端以自动注入JWT令牌。 但它被Grpc.Core中的一个例外“提供的通道凭据不允许组合”窒息 这是什么意思?我应该如何提供HTTP授权标头?

  • Hibernate 的常用配置文件主要分为 2 种:核心配置文件(hibernate.cfg.xml)和映射文件(Xxx.hbm.xml),它们主要用于配置数据库连接、事务管理、Hibernate 本身的配置信息以及 Hibernate 映射文件信息。 本节我们只讲解 Hibernate 核心配置文件,也即 hibernate.cfg.xml,后续将在《 Hibernate 映射文件》一节中继续讲

  • 我读过关于.NET标准和.NET核心之间的区别,但我真的不知道区别是什么,也不知道什么时候选择.NET标准库项目,什么时候选择.NET核心库项目。

  • ASP.NETCore支持一个新的配置系统,如下所示:https://docs.asp.net/en/latest/fundamentals/configuration.html 也支持这种模式吗?NET核心控制台应用程序? 如果不是,什么是替代以前的和模型?

  • 核心代码 核心代码基本都在 mininet/ 子目录下。 注:最新的 2.1.0 版本,核心 Python 代码仅为 4675 行。 $ find mininet -name "*.py" | xargs cat | wc -l