ASP.NET CORE 配置选项

内容纲要

以前提起配置,大家都会想到web.config和app.config,而且好像只有这一种方式。但是在ASP.NET CORE中,不仅提供了多元化的配置方式,而且提供了监视功能。

我们这里只介绍几种最常用的配置方式,最常见的配置方式应该还是配置文件吧,毕竟使用这种方式修改配置比较方便,不需要重新编译代码。

1、将配置定义在appsettings.json文件中

配置如下:

"format": {
    "dateTime": {
      "longDataPattern": "dddd,MMMM,d,yyyy",
      "longTimePattern": "h:mm:ss tt",
      "shortDataPattern": "M/d/yyyy",
      "shortTimePattern": "h:mm tt"
    },
    "currencyDecimal": {
      "digits": "5",
      "symbol": "$"
    }
  }

然后定义一个与当前结构一致的类:

public class FormatOptions
    {
        public DateTimeFormatOptions DateTime { get; set; }
        public CurrencyDecimalFormatOptions CurrencyDecimal { get; set; }     
    }
public class DateTimeFormatOptions
    {    
        public string LongDataPattern { get; set; }
        public string LongTimePattern { get; set; }
        public string ShortDataPattern { get; set; }
        public string ShortTimePattern { get; set; }
    }
public class CurrencyDecimalFormatOptions
    {
        public int Digits { get; set; }
        public string Symbol { get; set; }    
    }

在startup的ConfigureServices方法中添加依赖注入:

services.AddOptions().Configure(Configuration.GetSection("format"));

这里的Configuration就是startup类的全局变量

public IConfiguration Configuration { get; }

这个Configuration默认包含了appsettings.json配置文件的内容,所以我们不需要自己新建一个ConfigurationBuilder对象,如果我们的配置不是定义在appsettings.json里面,而是自定义了一个JSON文件,

那我们就需要新建一个ConfigurationBuilder对象,这个我们下面会讲到

这里为什么要调用GetSection方法呢?因为我们的appsettings.json中还有其他的配置项(项目创建时框架默认创建的配置项),我们只取我们想要的部分,如果这里不调用GetSection的话,也无法获取到

配置项的值,因为我们定义的类里面没有框架默认的那些配置项的字段。

下面我们新建一个控制器,把IOptions作为构造函数的参数,代码如下:

public class TestController : Controller
    {
        private readonly IOptions<FormatOptions> _options;
        public TestController(IOptions<FormatOptions> options)
        {
            _options = options;
        }
        public string Index()
        {
            var value = _options.Value;
            var dateTime = value.DateTime;
            var currencyDecimal = value.CurrencyDecimal;
            StringBuilder sb = new StringBuilder();
            sb.Append($"DateTime:");
            sb.Append($"\r\nlongDataPattern:{dateTime.LongDataPattern}");
            sb.Append($"\r\nlongTimePattern:{dateTime.LongTimePattern}");
            sb.Append($"\r\nshortDataPattern:{dateTime.ShortDataPattern}");
            sb.Append($"\r\nshortTimePattern:{dateTime.ShortTimePattern}");
            sb.Append($"\r\nCurrencyDecimal:");
            sb.Append($"\r\ndigits:{currencyDecimal.Digits}");
            sb.Append($"\r\nsymbol:{currencyDecimal.Symbol}");
 
            return sb.ToString();
        }
    }

2、自定义配置文件

某些情况下,我们可能需要将配置文件定义在我们自定义的JSON文件中。我们自己新建一个profile.json文件,配置如下:

{
  "gender": "Male",
  "age": "33",
  "contactinfo": {
    "emailAddress": "foobar.outlook.com",
    "phoneno": "123456789"
  }
}

在startup类的ConfigureServices方法中添加依赖注入:

var configuration = new ConfigurationBuilder().AddJsonFile(path:"profile.json",optional:false,reloadOnChange:true).Build();
services.AddOptions().Configure<Profile>(configuration);

path:表示配置文件的路径;optional表示这个配置文件是不是可选的,如果是的话,在程序启动的时候框架就不会去检查该文件是不是存在,而且就算没有这个文件,程序也不会报错,框架也会给这个配置对应的类的所有字段赋一个默认值。如是不是可选的话,框架会检查这个文件是不是存在,不存在的话会报错;reloadOnChange:如果配置文件有改动的话,框架会重新加载配置文件;

这个profile类的定义就按照配置文件给出的字段定义就好了,这些我就不把这个类的定义贴出来了。使用的方式也和上面介绍的FormatOptions一样。如果这个配置文件里面还有其他的配置项,

记得调用GetSection方法获取我们想要的部分。

3、根据运行环境动态加载配置文件

在ASP.NET CORE中有3中运行环境:Development、Stage、Production。不同环境下配置文件的内容可能会不同。

我们拿发布环境做一下测试。如果当前项目还没有appsettings.Production.json文件,那就新建一个。

配置如下:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "format": {   
    "currencyDecimal": {
      "digits": "6"   
    }
  }
}
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            var formatConfig = new ConfigurationBuilder().AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile(path: $"appsettings.{environmentName}.json", optional: false, reloadOnChange: true).Build() ;
services.AddOptions().Configure<FormatOptions>(formatConfig.GetSection("format"));

大家可以看到在appsettings.Production.json配置文件中,我并没有定义FormatOptions的所有字段,只配置了CurrencyDecimalFormatOptions类里面的Digits字段。

在创建ConfigurationBuilder类的时候,我添加了2个appsettings.json,在appsettings.json中,我定义了FormatOptions的所有字段。

在appsettings.json里面我把Digits设置为4,而在appsettings.Production.json里面我把Digits设置为6。然后在lanuchSettings.json中把ASPNETCORE_ENVIRONMENT

设置为Production,还是在TestController中运行,我们发现Digits字段的值等于6。

这种方式我觉得还是挺好的,把所有配置项都写在appsettings.json文件中,然后在与运行环境相对应的配置文件中只写运行环境对应的配置项,不需要再把所有的

配置项都重写一遍。框架会把这两个文件的配置项做一个合并。

4、监视配置文件的变化

我们都希望在程序运行的过程中修改配置文件不需要重启应用程序,修改的配置也能生效,ASP.NET CORE做到了这一点。

代码其实和上面的差不多,我们要监视配置文件的变化,只需要把reloadOnChange属性设置为true即可。

然后我们在消费Options对象的时候,要使用IOptionsMonitor,而不是之前的IOptions,代码如下:

public TestController(IOptions<FormatOptions> options,IOptionsMonitor<FormatOptions> optionsMonitor)
        {
            _options = options;
            _monitorOptions = optionsMonitor;
        }

我新加了一个函数:

public string MonitorIndex()
        {
            var value = _monitorOptions.CurrentValue;
            var dateTime = value.DateTime;
            var currencyDecimal = value.CurrencyDecimal;
            StringBuilder sb = new StringBuilder();
            sb.Append($"DateTime:");
            sb.Append($"\r\nlongDataPattern:{dateTime.LongDataPattern}");
            sb.Append($"\r\nlongTimePattern:{dateTime.LongTimePattern}");
            sb.Append($"\r\nshortDataPattern:{dateTime.ShortDataPattern}");
            sb.Append($"\r\nshortTimePattern:{dateTime.ShortTimePattern}");
            sb.Append($"\r\nCurrencyDecimal:");
            sb.Append($"\r\ndigits:{currencyDecimal.Digits}");
            sb.Append($"\r\nsymbol:{currencyDecimal.Symbol}");
 
            return sb.ToString();
        }

唯一的变化就是获取值的时候使用的CurrentValue属性。

在浏览器中输入/test/MonitorIndex进入到该方法,然后到程序运行的根目录下(bin\Debug\netcoreapp3.1)修改任意一个配置项的值,保存。

然后刷新一下,界面就会显示新值。如果我们还是进入到/test/index,发现修改过的配置项的值是不会变的。

下面是配置项对应POCO对象:

public class Profile
{
        public Gender Gender { get; set; }
        public int Age { get; set; }
        public ContactInfo ContactInfo { get; set; }
}
public class ContactInfo
{
        public string EmailAddress { get; set; }
        public string PhoneNo { get; set; }      
}

1、IOptionsSnapshot

在上一篇文章中我们介绍了IOptions和IOptionsMonitor,这里我们讲一下IOptionsSnapshot。我们看一下下面这个配置文件:

{
  "foo": {
    "gender": "Male",
    "age": "18",
    "contactinfo": {
      "emailAddress": "foobar.outlook.com",
      "phoneno": "123"
    }
  },
  "bar": {
    "gender": "Female",
    "age": "20",
    "contactinfo": {
      "emailAddress": "foobar.outlook.com",
      "phoneno": "456"
    }
  }
}

这里有2个配置,配置项的字段是一样的,但是每个配置都有自己的名称:foo和bar。这就像是一个班级里面有很多学生,每个学生都拥有自己的属性(姓名、年龄等)。

这种配置的使用方式和上篇文章介绍了那两种其实区别不大,下面看一下代码:

这是在startup的ConfigureServices函数中的代码:

var configuration1 = new ConfigurationBuilder().AddJsonFile(path: "profile1.json", optional: false, reloadOnChange: true).Build();
            services.AddOptions().Configure<Profile>("foo", configuration1.GetSection("foo"));
            services.AddOptions().Configure<Profile>("bar", configuration1.GetSection("bar"));

这是获取配置的代码:

var fooProfile = _snapOptions.Get("foo");
var barProfile = _snapOptions.Get("bar");

_snapOptions是定义的全局变量:IOptionsSnapshot _snapOptions,通过依赖注入给它赋值。

这种方式其实是给配置取了一个名字,然后通过名字去获取配置。

2、MemoryConfigurationSource

前面我们都是用配置文件,因为这是最常见一种配置方式,下面我们看一下使用内存来配置。

var memorySource = new Dictionary<string, string>
            {
                ["gender"] = "Male",
                ["age"] = "20",
                ["ContactInfo:emailaddress"] = "foo@outlook.com",
                ["ContactInfo:phoneno"] = "123"
            };
 
var memeoryConfig = new ConfigurationBuilder().AddInMemoryCollection(memorySource).Build();
services.AddOptions().Configure<Profile>(memeoryConfig);

gender和age是Profile类的属性,所以直接赋值即可。但是后面两个是ContactInfo类的属性,所以需要把ContactInfo放到前面,然后用冒号引用这个类的属性。这里注意一定是冒号,而不是逗号。

获取配置的方式和前面一样,这里就不再赘述了。

给TA打赏
共{{data.count}}人
人已打赏
.Net Core

在ASP.NET Core 6的最小API中使用日志和DI

2022-8-1 17:44:40

.Net Core

如何在ASP.NET Core 6中使用跟踪监听器

2022-8-3 16:56:11

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索