ASP.NET Core —配置系统

这篇具有很好参考价值的文章主要介绍了ASP.NET Core —配置系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为。

配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code (硬编码)的方式写在应用代码中,这样配置就会很乱,而且后续也不容易修改。乱而多,而且不容易修改,这就需要一个统一管理的地方,最常见的方式就是配置文件,这个也是开发人员非常熟悉的方式。

通过配置文件设置好软件应用运行的各种参数之后,我们在开发过程中需要能够读取到配置文件的内容,根据配置内容进行软件逻辑的判断,实现完善的软件行为逻辑。这一篇就是介绍 .NET Core 框架下怎么使用配置系统,这也是 .NET Core 下的基础设施之一。

1. 配置读取

配置读取是配置系统最基本的操作,几乎是每个开发人员都会进行的操作,一个开发人员可能不清楚配置系统是怎么实现的,配置文件是怎么解析的,但一定都做过读取配置信息的操作。.NET Core 框架下对于配置系统的使用最终暴露出来的接口是 IConfiguration,它是供配置数据的统一视图,配置读取就通过这个接口的实现来进行。

默认创建的 ASP .NET Core 框架模板项目中默认有一个appsettings.json 配置文件,这个也是 ASP.NET Core 中最常用的配置文件,在配置文件中添加多一个 Settings 节点,内容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Settings": {
    "key1": "value1",
    "key2": 1,
    "key3": true,
    "key4": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

我们要读取配置文件中的内容,例如读取 “AllowedHosts” 对于的值,只需要将其注入到需要的服务类中即可使用,ASP.NET Core 模板项目中使用 Web 主机构建和管理应用,在使用主机默认配置的时候已经将 IConfiguration 服务注册到依赖注入容器之中。

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly IConfiguration _configuration;
    public WeatherForecastController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet]
    public Task Get()
    {
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
		// 配置键不区分大小写
		var allowedHosts = _configuration["AllowedHosts"];
		Console.WriteLine(allowedHosts);
        return Task.CompletedTask;
    }
}

上面这种读取方式是索引器方式,最简单也是基本的方式,配置被加载到内存中是以键值对的方式存在的,我们可以通过配置键读取配置值,键是字符串,不区分大小写,读取出来的值都是字符串。

配置文件中配置值往往不止一层,就像上面 appsettings.json 文件中,Logging 节点下还有子节点,如果需要这种分层数据,可以使用 : 字符(英文冒号)分隔层次结构,例如获取上面配置键 Default 对于的配置值。

// 以 : 作为分隔符,表示层级结构
var defalutLogLevel = _configuration["Logging:LogLevel:Default"];

如果配置值是数组,需要读取数组中具体的某一个值,可以用该值在数组中的索引作为 key,例如读取上面配置文件中的 items 数组中的 item2。

// 读取数组,可以用值在数组中的索引作为key
var item2 = _configuration["Settings:items:1"];

这种方式读取配置有挺多不方便的地方,例如配置值是数值型的时候,需要我们直接转换,例如一次只能读取到一个配置值。微软通过 Microsoft.Extensions.Configuration.Binder 中的 ConfigurationBinder类提供了一些 IConfiguration 的静态方法,用于获取配置值时进行自动转换和绑定。

(1) GetValue

通过 ConfigurationBinder 中的 GetValue 扩展方法,一样可以通过配置键从配置系统中读取对于的配置值。该方法有多个重载,支持通过泛型的方式进行数据类型转换,并且支持设置默认值。

var defaultLogLevel2 = _configuration.GetValue<string>("Logging:LogLevel:Default");
// 配置信息中不包含 "Logging:LogLevel:Default" 这个Key时,以默认值 "Error" 返回
var defaultLogLevel3 = _configuration.GetValue<string>("Logging:LogLevel:Default", "Error");

(2) GetSection

这样子有些情况下仍然无法满足我们的需要,某一些情况下我们会需要直接读取配置中的一部分节点,例如直接读取上面配置中的 LogLevel 部分。IConfiguration 中的 GetSection 方法可以通过 Key 直接读取某一个子节点。该方法的返回值是 IConfigurationSection 类型,永远不会返回null,IConfigurationSection 实际上是一个 IConfiguration 的派生接口,也就是说我们还可以从 IConfigurationSection 再去获取我们需要的具体的配置值。

var section = _configuration.GetSection("Settings:key4");
var defaultLogLevel4 = section["Default"];

(3) Get

上面讲到通过 GetSection 获取到了配置文件中的一部分子节点,但是那样仍然不方便,还是需要一个一个去读取具体的值。可以通过 ConfigurationBinder.Get 扩展方法,将配置以强类型的方式绑定到对象上。

首先需要定义一个类来接收配置文件中的节点信息

public class KeyOptions
{
    public string subKey1 { get; set; }

    public int subKey2 { get; set; }
}

然后通过以下方式进行绑定:

var keyOption1 = _configuration.GetSection("Settings:key4").Get<KeyOptions>();

(4) Bind

ConfigurationBinder.Bind 扩展方法与 Get 方法类似,也是用于将配置绑定为强类型对象,不过 Bind的 方法是绑定到一个已实例化的对象上,需要提供一个已存在的对象。

var keyOption2 = new KeyOptions();
_configuration.GetSection("Settings:key4").Bind(keyOption2);

(5) Exists

上面说过,GetSection 方法获取配置中的子节点,返回值永远不会为 null。如果我们传入了一个不存在的 key,肯定是获取不到对于的值的,这种情况下还是需要判断对于的子节点到底是不是真正存在的,这时候可以使用 Exists 方法。

var section = _configuration.GetSection("settings");
var exist = section.Exists();

除此之外,还有一个 GetChildren 方法,无需参数,用于获取到当前配置节点的所有直接子节点的集合。

以上就是 .NET Core 体系下配置系统读取配置的基本介绍,涉及到的类型最主要的是 IConfiguration 接口,除此之外还有上面提到的 IConfigurationSection 接口,以及 IConfigurationRoot 接口。

IConfigurationRoot 表示配置的根节点,是 IConfiguration 的派生接口,以下为接口的定义:

public interface IConfigurationRoot: IConfiguration{
	// 存放了当前应用程序的所有配置提供程序
	IEnumerable<IConfigurationProvider> Providers { get; }
	// 强制从配置提供程序中重载配置
	void Reload();
}

这里可以看到一个关键的属性 IEnumerable<IConfigurationProvider> Providers,这个就是配置系统中的配置信息的来源,后面会仔细讲这个。而 Reload 方法中最关键的也是调用集合中各个 IConfigurationProvider 进行数据加载。

IConfigurationSection 表示配置中的子节点,也是 IConfiguration 的派生接口,以下为接口的定义:

public interface IConfigurationSection: IConfiguration{
	// 该子节点在其父节点中所表示的 key,即直接对应的key
	string Key { get; }
	// 该子节点在配置中的全路径(从根节点开始,到当前节点以:符号分隔的路径)
	string Path { get; }
	// 该子节点的 value。如果该子节点是叶子节点,则Value为该节点对于的值,若其下存在子节点,则其始终为 null
	string Value { get; set; }
}

IConfigurationSection 接口通过以上三个属性,结合IConfiguration中的 GetChildren 方法来完整地表示的一个子节点,而 Exists 方法判断节点是否为空,就是针对 IConfigurationSection 中的Value属性和 GetChildren 方法来进行的。

public static class ConfigurationExtensions{
	public static bool Exists(thisIConfigurationSection section){
	    if(section == null)
	    {
	        returnfalse;
	    }
	    returnsection.Value != null || section.GetChildren().Any();
	}
}

2. 配置添加

配置系统可以读取到配置文件中的信息,那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到ASP.NET Core 入口文件中,builder(WebApplicationBuilder 对象) 中有一个 Configuration 属性,这里就是我们扩展添加额外的配置的地方。

查看 Configuration 属性,可以看到是 ConfigurationManager 类型,而 ConfigurationManager 实现了 IConfigurationBuilder 接口。
ASP.NET Core —配置系统
ASP.NET Core —配置系统
IConfigurationBuilder 接口定义如下:

public interface IConfigurationBuilder{
       // 存放用于该 Builder 的 Sources 列表中各个元素的共享字典
	IDictionary<string, object> Properties { get; }
	// 已注册的 IConfigurationSource 列表
	IList<IConfigurationSource> Sources { get; }
	// 将 IConfigurationSource 添加到 Sources 中
	IConfigurationBuilder Add(IConfigurationSource source);
	// 通过 Sources 构建配置提供程序实例,并创建 IConfigurationRoot 实例
	IConfigurationRoot Build();
}

而它的实现类 ConfigurationBuilder 就是配置系统的入口。

public class ConfigurationBuilder : IConfigurationBuilder
{
    public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>();

    public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();

    public IConfigurationBuilder Add(IConfigurationSource source)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        Sources.Add(source);
        return this;
    }

    public IConfigurationRoot Build()
    {
        var providers = new List<IConfigurationProvider>();
        foreach (IConfigurationSource source in Sources)
        {
            IConfigurationProvider provider = source.Build(this);
            providers.Add(provider);
        }
        return new ConfigurationRoot(providers);
    }
}

当我们启动一个 ASP.NET Core 应用的时候是创建并启动了一个 Web 主机,由 Web 主机来启动并管理我们的应用的生命周期,在这个过程中会默认添加一些配置提供程序,加载一些配置信息。这些操作就在以下代码中:

var builder = WebApplication.CreateBuilder(args);

.NET Core 框架下的主机除了适用于 Web 应用的 Web 主机之外,还有通用主机。若是在普通的控制台应用程序,想要通过主机启动应用,并使用配置系统可用以下方式:

(1) 添加 Microsoft.Extensions.Hosting Nuget 包
(2) 通过以下代码创建主机

using IHost host = Host.CreateDefaultBuilder(args).Build();
host.Run();

主机这块就先稍微了解以下,后面还会有专门的文章去介绍。除了通过主机的方式使用配置系统之外,我们也可以直接通过 ConfigurationBuilder 类构建,如下:

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

config.GetValue<string>("Logging:LogLevel:Default");

这里需要引入相应的 Nuget 包,例如使用 ConfigurationBinder 相关的扩展方法就要引入 Microsoft.Extensions.Configuration.Binder Nuget包,使用 Json 格式配置文件就要引入Microsoft.Extensions.Configuration.Json Nuget包,该Nuget 包中包含 AddJsonFile 扩展方法,通过指定文件路径添加相应的配置文件到配置系统中。

配置文件多种多样,比较常用的都有 Json 、xml、ini、yaml 等多种,甚至配置信息不一定存储在文件之中。.NET Core 配置系统对各种来源的配置信息进行了抽象,不同来源只要提供相应的配置提供程序即可,也就是我们上面在 IConfigurationRoot 接口中看到的 IConfigurationProvider 接口的实现类。配置提供程序内部对不同来源不同格式的配置信息进行加载、刷新,并提供统一的访问方式,也就是键值对,实际上所有的配置信息最终会以键值对的方式被读取到内存中的 Dictionary 对象中。

我们要添加不同类型不同来源的配置信息,只需要通过 IConfigurationBuilder 实现类对象添加不同的配置提供程序即可。在 ASP.NET Core 应用之中,可以通过以下方式进行添加:

// 添加一个xml配置文件,并加入到配置系统中
var configirationFilePath = Path.Combine(Directory.GetCurrentDirectory(), "xxx.xml");
builder.Configuration.AddXmlFile(configirationFilePath);

也可以通过以下方式:

builder.WebHost.ConfigureAppConfiguration(builder =>
{
    builder.AddXmlFile("");
});

这两种方式是一样的效果的,只不过具体的实现类不同,ConfigureAppConfiguration 可以将原有的配置提供程序情况,而 builder.Configuration 则不行,只能往集合后面继续添加。

3. 配置提供程序

上面提到,通过 IConfigurationBuilder 的实现类对象,我们可以自由地往配置系统中添加不同的配置提供程序,从而获取不同来源的配置信息。.NET Core 中,微软提供了以下这些内置的配置提供程序:

○ 文件配置提供程序
○ 环境变量配置提供程序
○ 命令行配置提供程序
○ Azure应用配置提供程序
○ Azure Key Vault 配置提供程序
○ Key-per-file配置提供程序
○ 内存配置提供程序
○ 应用机密(机密管理器)
○ 自定义配置提供程序

这里稍微介绍一下常用的几个。

3.1 文件配置提供程序

顾名思义,这个就是我们熟悉的配置加载方式,从配置文件中加载配置信息。配置文件多种多样,.NET Core 框架内置支持 Json、xml、ini 三种格式的文件提供程序:

  • JSON配置提供程序(JsonConfigurationProvider)
  • XML配置提供程序(XmlConfigurationProvider)
  • INI配置提供程序(IniConfigurationProvider)

以上这些配置提供程序,均继承于抽象基类 FileConfigurationProvider,当一个提供程序中发现重复的键时,提供程序会引发 FormatException,所有类型的文件提供程序都是这样的机制。

另外,所有文件配置提供程序都支持提供两个配置参数:

  • optional:bool 类型,指示该文件是否是可选的。如果该参数为false,但是指定的文件又不存在,则会报错。
  • reloadOnChange:bool 类型,指示该文件发生更改时,是否要重新加载配置。

3.1.1 JSON配置提供程序

JSON 配置提供程序被封装在 Microsoft.Extensions.Configuration.Json Nuget包中,若通过 ConfigurationBuilder 自行构建配置系统需要先安装该依赖包。它通过 JsonConfigurationProvider 在运行时从 Json 文件中加载配置。

使用方式非常简单,通过 IConfigurationBuilder 的实现类对象调用 AddJsonFile 扩展方法指定Json配置文件的路径即可。以下代码可用于控制台程序中创建主机并设置配置系统:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var env = context.HostingEnvironment;
        // 添加 json 配置文件
        config.AddJsonFile("appsettings.json",true, true)
            .AddJsonFile($"appsetting.{env.EnvironmentName}.json", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");

host.Run();

appsetting.json 配置文件中的内容如下:

{
  "Settings": {
    "Provider": "JsonProvider",
    "version": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

控制台程序运行之后输出如下:
ASP.NET Core —配置系统
这样有一点要注意的是,对于我们手动添加的配置文件需要设置一下文件属性,让其在项目生成的时候能够正常生成到运行目录,确保应用可以正常获取到该文件:

ASP.NET Core —配置系统

3.1.2 XML配置提供程序

XML 配置提供程序被封装在 Microsoft.Extensions.Configuration.Xml Nuget包中,通过 XmlConfigurationProvider 类在运行时从 XML 文件加载配置。

使用方式也很简单,与 JSON 配置提供程序类似,通过 AddXmlFile 扩展方法指定配置文件路径。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var env = context.HostingEnvironment;
         添加 json 配置文件
        //config.AddJsonFile("appsettings.json",true, true)
        //    .AddJsonFile($"appsetting.{env.EnvironmentName}.json", true, true);

        config.AddXmlFile("appsettings.xml", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

xml 配置文件内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<Settings>
		<Provider>XmlProvider</Provider>
		<version>
			<subKey1>value</subKey1>
			<subKey2>1</subKey2>
		</version>
		<items>item1</items>
		<items>item2</items>
		<items>item3</items>
	</Settings>
</configuration>

运行程序控制台输出如下:
ASP.NET Core —配置系统
这里有一个和版本有关的点,对 Xml 文件中使用同一元素名称的重复元素,一般也就是数组,.NET 6及之后的xml 配置提供程序会自动为其编制索引,不再需要显式指定name属性。如果是 .NET 6 以下的版本则需要这样了:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<Settings>
		<Provider>XmlProvider</Provider>
		<version>
			<subKey1>value</subKey1>
			<subKey2>1</subKey2>
		</version>
		<items name="itemkey1">item1</items>
		<items name="itemkey2">item2</items>
		<items name="itemkey3">item3</items>
	</Settings>
</configuration>
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:itemkey2")}");

另外 xml 文件中的属性也可用于提供值:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <key attribute="value" />
  <section>
    <key attribute="value" />
  </section>
</configuration>

获取属性的值可用以下配置键:

key:attribute
section:key:attribute

3.1.3 INI配置提供程序

INI 配置提供程序被封装在 Microsoft.Extensions.Configuration.Ini Nuget包,通过 IniConfigurationProvider 类在运行时从 INI 文件加载配置。使用方式如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddIniFile("appsettings.ini", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ini 配置文件内容如下:

[Settings]
Provider="IniProvider"
version:subKey1="value"
version:subKey2=1
items:0="item1"
items:1="item2"
items:3="item3"

运行应用,控制台输出如下:

ASP.NET Core —配置系统

3.2 环境变量配置提供程序

环境变量配置提供程序被封装在 Microsoft.Extensions.Configuration.EnvironmentVariables, 通过 EnvironmentVariablesConfigurationProvider 在运行时从环境变量中以键值对的方式加载配置。

环境变量一般情况下是配置在机器上的,而不同的操作系统对环境变量的设置要求有所不同,当环境变量存在多层的时候,层级之间的分隔有些支持通过 : 号进行分隔,有些不支持,双下划线 __ 是全平台支持的,所以设置环境变量的时候要使用双下划线 __ 来代替冒号 :

各种不同的平台下怎么去添加环境变量这里就不细说了,Windows 下大家最起码都应该知道可以通过 我的电脑 -> 属性 -> 高级系统设置 去可视化的添加,命令行的方式可阅读下官方文章: ASP.NET Core 中的配置 | Microsoft Learn,Linux 平台下可以通过 export 命令临时添加,或者修改相应的配置文件 ~/.bashrc/etc/profile,大家仔细查一下资料就行了。

处理在机器上直接设置环境变量外,我们开发测试的过程中也可以通过 ASP.NET Core 框架下的 launchSettings.json 配置文件设置用于调试的临时环境变量。在应用启动调试时,该文件中的环境变量会替代系统的中的环境变量。

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "ConfigurationSample": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "http://localhost:5004",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "Custom_settings__Provider": "EnvironmentVariablesProvider",
        "Custom_settings__version__subKey1": "value",
        "Custom_settings__items__0": "item1",
        "Custom_settings__items__1": "item2",
        "Custom_settings__items__2": "item3"
      }
    }
  }
}

环境变量配置提供程序使用也很简单,注意以下示例为了使用 launchSettings.json 中的环境变量是在 ASP.NET Core 项目中测试的。

var builder = WebApplication.CreateBuilder(args);

builder.Host.ConfigureAppConfiguration(builder =>
{
    builder.Sources.Clear();
    // 筛选前置为 Custom_ 的环境变量,将其加载为应用配置,其他的不加载
    builder.AddEnvironmentVariables("Custom_");
});

var app = builder.Build();

Console.WriteLine($"Settings:Provider: {app.Configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {app.Configuration.GetValue<string>("Settings:items:1")}");

app.Run();

在添加环境变量时,通过指定参数 prefix,只读取限定前缀的环境变量。不过在读取环境变量时,会将前缀删除。如果不指定参数 prefix,那么会读取所有环境变量。

当创建默认通用主机(Host)时,默认就已经添加了前缀为DOTNET_的环境变量,如果是在 ASP.NET Core 中,配置了 Web 主机时,默认添加了前缀为 ASPNETCORE_ 的环境变量,而后主机加载应用配置时,再根据策略添加了其他的环境变量,如果没有传递 prefix 参数则是所有环境变量。这一块的加载机制,下面再细讲。

运行应用,控制台输出如下:
ASP.NET Core —配置系统
除此之外,环境变量提供程序还有一些隐藏的功能点,当没有向 AddEnvironmentVariables 传入前缀时,默认也会针对含有以下前缀的环境变量进行特殊处理:
ASP.NET Core —配置系统
这个功能点比较少用到,但是大家看到这个大概都会有点疑惑,具体的形式是怎么样的,下面稍微测试一下

首先在 launchSettings.json 文件中添加多一个环境变量:

"MYSQLCONNSTR_Default": "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"

之后在应用中打印如下两个配置:

Console.WriteLine($"ConnectionStrings:Default: { app.Configuration.GetValue<string>("ConnectionStrings:Default") }");
Console.WriteLine($"ConnectionStrings:Default_Provider: { app.Configuration.GetValue<string>("ConnectionStrings:Default_ProviderName") }");

输出结果如下:
ASP.NET Core —配置系统
也就是说,这种形式的环境变量会被自动转换为两个。

3.3 命令行配置提供程序

命令行配置提供程序被封装在 Microsoft.Extensions.Configuration.CommandLine 包中,通过 CommandLineConfigurationProvider 在运行时从命令行参数键值对中加载配置。

当我们通过 dotnet 命令启动一个 .NET Core 应用时,我们可以在命令后面追加一些参数,这些参数将在入口文件中被 args 变量接收到。命令行配置提供程序使用如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddCommandLine(args);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

之后通过命令行程序启动应用,并传入相应的参数:

dotnet ConfigurationSampleConsole.dll Settings:Provider=CommandLineProvider Settings:items:1=item1

ASP.NET Core —配置系统
命令行参数的设置有三种方式:

(1) 使用 = 号连接键值:

dotnet ConfigurationSampleConsole.dll Settings:Provider=CommandLineProvider Settings:items:0=item1 Settings:items:1=item2

(2) 使用 / 号表示键,值跟在键后面,键值以空格分隔

dotnet ConfigurationSampleConsole.dll /Settings:Provider CommandLineProvider /Settings:items:0 item1 /Settings:items:1 item2

(3) 使用 – 符号表示键,值跟在键后面,键值以空格分隔

dotnet ConfigurationSampleConsole.dll --Settings:Provider CommandLineProvider --Settings:items:0 item1 --Settings:items:1 item2

如果值之中本来就有空格的,可以使用 “” 号包括。

dotnet ConfigurationSampleConsole.dll --Settings:Provider CommandLineProvider --Settings:items:0 item1 --Settings:items:1 "test item2"

AddCommandLine 扩展方法提供了重载,允许额外传入一个参数,该参数提供一个交换映射字典,针对命令行配置参数进行key映射。例如命令行传入键是 name01 ,映射后的的键为 project:name。这里有一些要注意的点:

  • 交换映射key必须以---开头。当使用-开头时,命令行参数书写时也要以-开头,当使用--开头时,命令行参数书写时可以以--/开头。
  • 交换映射字典中的 key 不区分大小写,不能包含重复 key。如不能同时出现-n-N,但可以同时出现-n--n
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var switchMappings = new Dictionary<string, string>
        {
            ["--b1"] = "Settings:Provider",
            ["-b2"] = "Settings:items"
        };

        config.AddCommandLine(args, switchMappings);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ASP.NET Core —配置系统

3.4 内存配置提供程序

内存配置提供程序就比较简单了,它直接被包含在 Microsoft.Extensions.Configuration,通过 MemoryConfigurationProvider 在运行时从内存中的集合中加载配置。使用方式如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddInMemoryCollection(new Dictionary<string, string> {
            { "Settings:Provider", "InMemoryProvider" },
            { "Settings:items:1", "MemoryItem" }
        });
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ASP.NET Core —配置系统

3.5 配置加载顺序

上面介绍了一些常用的配置提供程序,这些配置提供程序都是通过扩展方法添加到 ConfigurationBuilder 对象中的,而从上面 ConfigurationBuilder 的源码可以看出,添加一个配置提供程序的时候其实应该是添加了一个对应的 IConfigurationSource 对象,而后在 ConfigurationBuilder 中被保存到集合中。

这就可以看出,配置系统是允许同时添加多种配置提供程序,支持多来源的配置信息同时存在的。那么当多个配置处理程序都被添加到配置系统之中,那我们从配置系统中通过配置键获取配置值的时候是怎么进行的呢,当多个配置提供程序存在相同的配置键时,我们获取到的配置值是哪个呢?

从 ConfigurationRoot 的源码中可以可以看到,当我们用索引器API读取配置值时,是调用了 GetConfiguration 方法

ASP.NET Core —配置系统
而GetConfiguration方法中的逻辑也很简单,只是遍历提供程序集合尝试从提供程序去获取值,需要关注的是遍历的顺序。
ASP.NET Core —配置系统
这里的逻辑是这样子的,倒叙进行遍历,后添加的配置处理程序先被遍历,一旦通过key从提供程序中获取到值就返回结果,不再继续遍历。所以添加配置提供程序的顺序决定相同配置键最终的值, 当多个配置处理程序存在相同键时,越后添加的配置提供程序优先级越高,从最后的一个提供程序获取到值之后就不再从其他处理程序获取。

3.6 默认配置来源

上面也有提到通过主机运行和管理应用,在通过主机运行的项目中,主机在启动的时候就已经默认添加了一些配置提供程序,所以我们创建了一个 ASP.NET Core 模板项目之后就可以获取到 appsettings.json 等配置文件中的配置信息。下面介绍一下默认添加的配置提供程序。

在 Host.CreateDefaultBuilder(String[]) 方法或者 WebApplication.CreateBuilder(args) 方法执行的时候,会按照以下顺序添加应用的配置提供程序:

(1) 内存配置提供程序
(2) Chained 配置提供程序(添加现有的主机配置)
(3) JSON 配置提供程序 (添加 appsettings.json 配置文件)
(4) JSON 配置提供程序 (添加 appsettings.{Environment}.json 配置文件)
(5) 机密管理器(仅Windows)
(6) 环境变量配置提供程序 (未限定前缀)
(7) 命令行配置提供程序

配置分主机配置和应用配置,主机启动时应用仍未启动,主机启动过程中的配置就是主机配置。上面第一个Chained 配置提供程序就是承接过来的主机配置。而主机配置是按照以下顺序加载的:

(1) 环境变量配置提供程序(以 DOTNET_ 为前缀的环境变量)
(2) 命令行配置提供程序 (命令行参数)
(3) 环境变量配置提供程序(以 ASPNETCORE_ 为前缀的环境变量,如果是Web主机的话)

所以最终的应用配置加载顺序应该是下面这样:

(1) 内存配置提供程序
(2) 环境变量配置提供程序(以 DOTNET_ 为前缀的环境变量)
(3) 命令行配置提供程序 (命令行参数)
(4) 环境变量配置提供程序(以 ASPNETCORE_ 为前缀的环境变量,如果是Web主机的话)
(5) JSON 配置提供程序 (添加 appsettings.json 配置文件)
(6) JSON 配置提供程序 (添加 appsettings.{Environment}.json 配置文件)
(7) 机密管理器(仅Windows)
(8) 环境变量配置提供程序 (未限定前缀)
(9) 命令行配置提供程序 (命令行参数)

按照越后面添加的提供程序优先的方式,最终应用配置会覆盖主机配置,并且最优先是最后添加的命令行配置提供程序,我们可以通过以下方式打印配置系统中所有的配置提供程序,进行验证:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

var configurationRoot = (IConfigurationRoot)app.Configuration;
foreach (var provider in configurationRoot.Providers.AsEnumerable())
{
    Console.WriteLine(provider.ToString());
}

app.Run();

最终控制台打印出来的结果如下:
ASP.NET Core —配置系统
虽然应用配置优先,会覆盖前面的主机配置,但是有一些变量会在初始化主机生成器的时候就提前进行锁定,并且之后不会受应用配置的影响:

  • 应用程序名称
  • 环境名称,例如 Development、Production 和 Staging
  • 内容根目录
  • Web 根目录
  • 是否要扫描托管启动程序集以及要扫描哪些程序集。
  • 应用和库代码从 IHostBuilder.ConfigureAppConfiguration 回调中的 HostBuilderContext.Configuration 读取的变量。

这里提到环境名称,其实也就是软件运行的环境,最最基本的也会分为开发环境、生产环境两种。软件运行环境通过环境变量来设置,普通的 .NET Core 应用环境变量 key 为NETCORE_ENVIRONMENT,Web 应用环境变量 key 为ASPNETCORE_ENVIRONMENT,Web 应用下如果两者同时存在,后者会覆盖前者。软件应用根据不同的环境会有不同的行为逻辑,例如上面讲到的 appsettings.{environment}.json 根据环境而不同的配置文件,例如之前的 入口文件 文章中讲到的 Startup 文件根据不同环境的分离配置方式,而我们在代码中有时也会根据环境处理不同的逻辑,这时候我们可以注入 IHostEnvironment 服务,通过它获取当前应用的运行环境,入口文件中无论是 WebApplicationBuilder 对象还是 WebApplication 对象都包含该类型的属性。

通过环境变量设置当前运行环境,其实环境变量的值只是一个字符串,我们可以设置成任意值,这是运行的,.NET Core 框架下 IHostEnvironment 也能够正常加载到相应的环境名称,但是 .NET Core 默认只提供了对 DevelopmentProductionStaging 三种环境的判别,以及相应的处理逻辑和扩展方法,如果是其他的自定义环境则需要开发人员自行进行相应的处理了。和 .NET Core 应用环境相关的知识点大家可以看一下官方文档: 在 ASP.NET Core 中使用多个环境 | Microsoft Learn

除了上面的,其他还有一些主机配置,例如 URLS,但这个是可以通过应用配置设置的,读取相应的配置值时也应用从应用配置读取。

URLS 配置Web应用启动后的访问地址,这个配置可以多个地方设置,其中命令行参数最优先,其他地方设置的应该被命令行参数覆盖。但是如果通过 Kestrel 终结点方式设置了 Web 应用的访问地址,那 Kestrel 终结点的配置将覆盖其他所有的访问地址的配置。

如在 appsettings.json 中添加以下配置:

"Kestrel": {
  "Endpoints": {
    "Https": {
      "Url": "https://localhost:9999"
    }
  }
}

那么以下几种方式设置的 URLS 都会失效:

  • UseUrls
  • 命令行上的 --urls
  • 环境变量 ASPNETCORE__URLS

也就是说,就算我们用以下命令启动应用,应用最终的访问地址还是以 Kestrel 终结点配置的为准:

dotnet run --urls="https://localhost:7777"

Kestrel 配置与 URLS 配置不是一个参数,我们可以通过在命令行或者环境变量中设置 kestrel 中间点配置来覆盖 appsettings.json 中的,这又回到配置提供程序的优先级问题了。

set Kestrel__Endpoints__Https__Url=https://localhost:8888
	
dotnet run Kestrel__Endpoints__Https__Url=https://localhost:8888

在主机启动的逻辑中 Kestrel 具备更高的最终优先级,但是其实主机内部是先根据 URLS 创建了一个终结点,之后又替换为 Kestrel 配置的终结点的。通过应用启动时的控制台输出可以看出。
ASP.NET Core —配置系统
这种情况对于单机应用没有什么影响,但是对于使用自动服务发现的微服务架构而言就可能有问题了,可能导致注册服务注册中心的终结点是第一个,而后应用终结点又被改变,导致注册中心记录的服务终结点和实际的不一致。

4. 自定义配置提供程序

在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvider 接口实现,这两个接口在上面也有提到了。

IConfigurationSource

IConfigurationSource 负责创建 IConfigurationProvider 实现的实例。它的定义很简单,就一个Build方法,返回 IConfigurationProvider 实例:

public interface IConfigurationSource
{
    IConfigurationProvider Build(IConfigurationBuilder builder);
}

IConfigurationProvider

IConfigurationProvider 负责实现配置的设置、读取、重载等功能,并以键值对形式提供配置。

public interface IConfigurationProvider
{
    // 获取指定父路径下的直接子节点Key,然后 Concat(earlierKeys) 一同返回
    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
    
    // 当该配置提供程序支持更改追踪(change tracking)时,会返回 change token
    // 否则,返回 null
    IChangeToken GetReloadToken();

    // 加载配置
    void Load();

    // 设置 key:value
    void Set(string key, string value);

    // 尝试获取指定 key 的 value
    bool TryGet(string key, out string value);
}

像工作中常用的配置中心客户端,例如 nacos、consul,都是实现了对应的配置提供程序,从而将配置中心中的配置无缝地接入到 .NET Core 的配置系统中进行使用,和本地配置文件的使用没有分别。

如果我们需要封装自己的配置提供程序,推荐直接继承抽象类 ConfigurationProvider,该类实现了 IConfigurationProvider 接口,继承自该类只要实现 Load 方法即可,Load 方法用于从配置来源加载解析配置信息,将最终的键值对配置信息存储到 Data 中。这个过程中可参考一下其他已有的配置提供程序的源码,模仿着去写自己的东西。

在我们日常的系统平台中,总少不了数据字典这样一个功能,用于维护平台中一些业务配置,因为是随业务动态扩展和变动的,很多时候不会写在配置文件,而是维护在数据库中。以下以这样一个场景实现一个配置提供程序。

因为是以数据库作为载体来存储配置信息,所以第一步就是定义实体类

public class DataDictioaryDO
{
    public int Id { get; set; }

    public int? ParentId { get; set; }

    public string Key { get; set; }

    public string Value { get; set; }
}

数据字典支持多级级联,通过 ParentId 关联上一级,ParentId 为空的即为根节点,如存在下级节点则 Value 值可以为空,就算填写了也无效,最终呈现出来的就是一个树结构。

然后就是定义相应的数据库访问上下问 DataDictionaryDbContext

public class DataDictionaryDbContext : DbContext
{
	  public DbSet<DataDictioaryDO> DataDictioaries { get; set; }
	
	  public DataDictionaryDbContext(DbContextOptions<DataDictionaryDbContext> options) : base(options)
	  {
	  }
	  
	  protected override void OnModelCreating(ModelBuilder modelBuilder)
	  {
	      base.OnModelCreating(modelBuilder);
	      modelBuilder.Entity<DataDictioaryDO>().HasKey(e => e.Id);
	      modelBuilder.Entity<DataDictioaryDO>().Property(e => e.Value).IsRequired(false);
	  }
}

通过 DbContextOptions 交由外部去配置具体的数据库类型和连接字符串。

之后创建 IConfigurationSource 实现类,主要就是构造函数中需要传入数据库配置委托,并且在 Build 实例化EFDataDictionaryConfigurationProvider 对象。

public class EFDataDictionaryConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _action;

    public EFDataDictionaryConfigurationSource(Action<DbContextOptionsBuilder> action)
    {
        _action= action;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EFDataDictionaryConfigurationProvider(_action);
    }
}

之后通过继承 ConfigurationProvider 实现 EFDataDictionaryConfigurationProvider,主要逻辑就是从数据库获取对应的数据表,如果表中没有数据则插入默认数据,再通过相应的解析器解析数据表数据生成一个 Dictionary<string, string> 对象。

public class EFDataDictionaryConfigurationProvider : ConfigurationProvider
{
    Action<DbContextOptionsBuilder> OptionsAction { get; }

    public EFDataDictionaryConfigurationProvider(Action<DbContextOptionsBuilder> action)
    {
        OptionsAction = action;
    }

    public override void Load()
    {
        var builder = new DbContextOptionsBuilder<DataDictionaryDbContext>();

        OptionsAction(builder);

        using var dbContext = new DataDictionaryDbContext(builder.Options);
        if(dbContext == null)
        {
            throw new Exception("Null DB Context !");
        }

        dbContext.Database.EnsureCreated();

        if (!dbContext.DataDictioaries.Any())
        {
            CreateAndSaveDefaultValues(dbContext);
        }

        Data = EFDataDictionaryParser.Parse(dbContext.DataDictioaries);
    }

    private void CreateAndSaveDefaultValues(DataDictionaryDbContext context)
    {
        var datas = new List<DataDictioaryDO>
        {
            new DataDictioaryDO
            {
                Id = 1,
                Key = "Settings",
            },
            new DataDictioaryDO
            {
                Id = 2,
                ParentId = 1,
                Key = "Provider",
                Value = nameof(EFDataDictionaryConfigurationProvider)
            },
            new DataDictioaryDO
            { 
                Id = 3,
                ParentId = 1,
                Key = "Version",
                Value = "v1.0.0"
            }
        };

        context.DataDictioaries.AddRange(datas);
        context.SaveChanges();
    }
}

其中,解析器 EFDataDictionaryParser 的代码如下,主要就是通过递归的方式,通过树形数据的 key 构建构建完整的 key,并将其存入 Dictionary<string,string> 对象中。

internal class EFDataDictionaryParser
{
    private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    private readonly Stack<string> _context = new();
    private string _currentPath;

    private EFDataDictionaryParser() { }

    public static IDictionary<string, string> Parse(IEnumerable<DataDictioaryDO> datas) =>
        new EFDataDictionaryParser().ParseDataDictionaryConfiguration(datas);

    private IDictionary<string, string> ParseDataDictionaryConfiguration(IEnumerable<DataDictioaryDO> datas)
    {
        _data.Clear();

        if(datas?.Any() != true)
        {
            return _data;
        }

        var roots = datas.Where(d => !d.ParentId.HasValue);
        foreach (var root in roots)
        {
            EnterContext(root.Key);
            VisitElement(datas, root);
            ExitContext();
        }
        return _data;
    }

    private void VisitElement(IEnumerable<DataDictioaryDO> datas, DataDictioaryDO parent)
    {
        var children = datas.Where(d => d.ParentId == parent.Id);
        if (children.Any())
        {
            foreach (var section in children)
            {
                EnterContext(section.Key);
                VisitElement(datas, section);
                ExitContext();
            }
        }
        else
        {
            var key = _currentPath;
            if (_data.ContainsKey(key))
                throw new FormatException($"A duplicate key '{key}' was found.");

            _data[key] = parent.Value;
        }
    }

    private void EnterContext(string context)
    {
        _context.Push(context);
        _currentPath = ConfigurationPath.Combine(_context.Reverse());
    }

    private void ExitContext()
    {
        _context.Pop();
        _currentPath = ConfigurationPath.Combine(_context.Reverse());
    }
}

之后为这个配置提供程序提供一个扩展方法,方便之后的使用,如下:

public static class EFDataDictionaryConfigurationExtensions
{
    public static IConfigurationBuilder AddEFDataDictionaryConfiguration(this IConfigurationBuilder builder, 
        Action<DbContextOptionsBuilder> optionAction)
    {
        builder.Add(new EFDataDictionaryConfigurationSource(optionAction));
        return builder;
    }
}

之后在入口文件中将我们的配置扩展程序添加到配置系统中,并指定使用内存数据库进行测试

using ConfigurationSampleConsole.ConfigProvider;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddEFDataDictionaryConfiguration(builder =>
        {
            builder.UseInMemoryDatabase("DataDictionary");
        });
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:Version: {configuration.GetValue<string>("Settings:version")}");

host.Run();

最后的控制台输出结果如下:

ASP.NET Core —配置系统
以上就是 .NET Core 框架下配置系统的一部分知识点,更加详尽的介绍大家可以再看看官方文档。配置系统很多时候是结合选项系统仪器一起使用的,下一篇将介绍一下 .NET Core 框架下的选项系统。


参考文章:

ASP.NET Core 中的配置 | Microsoft Learn
配置 - .NET | Microsoft Learn
理解ASP.NET Core - 配置(Configuration) - xiaoxiaotank


ASP.NET Core 系列总结:
上一篇:ASP.NET Core — 依赖注入
下一篇:ASP.NET Core — 选项系统文章来源地址https://www.toymoban.com/news/detail-478223.html

到了这里,关于ASP.NET Core —配置系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ASP.NET Core】MVC过滤器:运行流程

    MVC 的过滤器(Filters)也翻译为“筛选器”。但是老周更喜欢翻译为“过滤器”,意思上更好理解。 既然都叫过滤器了,就是在MVC的操作方法调用前后进行特殊处理的类型。比如: a、此调用是否已授权? b、在模型绑定之前要不要修改数据源?(可能含有儿童不宜的数据)

    2024年02月05日
    浏览(62)
  • ASP.NET Core MVC -- 将视图添加到 ASP.NET Core MVC 应用

    右键单击“视图”文件夹,然后单击“添加”“新文件夹”,并将文件夹命名为“HelloWorld”。 右键单击“Views/HelloWorld”文件夹,然后单击“添加”“新项”。 在“添加新项 - MvcMovie”对话框中: 在右上角的搜索框中,输入“视图” 选择“Razor 视图 - 空” 保持“名称”框的

    2024年02月13日
    浏览(91)
  • asp.net core之实时应用

    本文将介绍ASP.NET Core SignalR,这是一个强大的实时通信库,用于构建实时、双向通信应用程序。我们将探讨SignalR的基本概念、架构和工作原理,并提供一些示例代码来帮助读者更好地理解和使用SignalR。 ASP.NET Core SignalR提供了一种简单而强大的方式来构建实时通信应用程序。

    2024年02月14日
    浏览(57)
  • 【ASP.NET Core 基础知识】--最佳实践和进阶主题--设计模式在ASP.NET Core中的应用

    一、设计模式概述 1.1 什么是设计模式 设计模式是在软件设计过程中反复出现的、经过验证的、可重用的解决问题的方法。它们是针对特定问题的通用解决方案,提供了一种在软件开发中可靠的指导和标准化方法。设计模式通常描述了一种在特定情景下的解决方案,包括了问

    2024年02月21日
    浏览(160)
  • ASP.NET Core教程:ASP.NET Core 程序部署到Windows系统

    本篇文章介绍如何将一个ASP.NET Core Web程序部署到Windows系统上。这里以ASP.NET Core WebApi为例进行讲解。首先创建一个ASP.NET Core WebApi项目,使用默认的Values控制器,这里使用Visual Studio 2019创建一个ASP.NET Core 3.1d的WebApi项目。 创建新项目的时候选项ASP.NET Core Web应用程序,如下图所

    2023年04月08日
    浏览(56)
  • ASP.NET Core 配置系列一

    A S P . N E T   C o r e   配 置 主 要 通 过 这 3 个 文 件 设 置 : 1   项 目 文 件 也 叫 . c s p r o j   文 件 2   P r o g r a m . c s 3   a p p s e t t i n g s . j s o n 这 些 配 置 告 诉 A S P . N E T   C o r e   应 用 程 序 基 于 用 户 的 交 互 是 如 何 工 作 的, 在 本 节 中 我 们 理 解 A S P .

    2024年02月03日
    浏览(108)
  • ASP.NET Core实时库SignalR简单应用

    SignalR 是用于构建需要实时用户交互或实时数据更新的Web 应用程序的一个开放源代码.NET 库。不仅仅用在Web应用中,后面会讲到它的应用范围。它简化了简化了构建实时应用程序的过程,包括 ASP.NET Server 库和 JavaScript Client 库,以便管理Client与Server连接并将内容更新推送给Cl

    2024年02月11日
    浏览(76)
  • ASP.NET Core如何知道一个请求执行了哪些中间件?

    需要添加两个Nuget包分别是: Microsoft.AspNetCore.MiddlewareAnalysis 和 Microsoft.Extensions.DiagnosticAdapter ,前者是分析记录中间件核心代码实现后者是用来接收日志输出的,由于是用的DiagnosticSource方式记录日志,所以需要使用DiagnosticListener对象的SubscribeWithAdapter方法来订阅。 这个适配器

    2023年04月09日
    浏览(69)
  • 一个基于ASP.NET Core完全开源的CMS 解决方案

    MixCore CMS是一个基于.NET Core框架的开源内容管理系统(CMS),提供了丰富的的基础功能和插件,是一款面向未来的企业 Web  CMS ,可轻松构建任何类型的应用程序。集成了Google Analytics分析,以及友好的Seo功能,非常适合用于创建企业网站、内容系统、个人博客,也可以用于开发

    2024年02月05日
    浏览(82)
  • 使用任务计划开机启动ASP.NET Core应用程序

    ASP.NET Core应用程序现在是一个控制台应用程序,在Windows上直接双击启动,但如果想让开发完成的ASP.NET Core应用程序开机启动,可以将ASP.NET Core应用程序修改成Windows服务运行,但这需要额外添加代码,也可以使用IIS来托管ASP.NET Core应用程序,但可能需要安装一些支持IIS的组件

    2024年02月08日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包