FsConfig 4.1.0

dotnet add package FsConfig --version 4.1.0                
NuGet\Install-Package FsConfig -Version 4.1.0                
该命令打算在 Visual Studio 的包管理器控制台中使用,因为它使用了 NuGet 模块版本的 Install-Package.
<PackageReference Include="FsConfig" Version="4.1.0" />                
对于支持 PackageReference 的项目,将此 XML 节点复制到项目文件中以引用包。
paket add FsConfig --version 4.1.0                
#r "nuget: FsConfig, 4.1.0"                
#r 指令可用于 F# Interactive 和多语言笔记本。将其复制到交互工具或脚本的源代码中以引用包。
// Install FsConfig as a Cake Addin
#addin nuget:?package=FsConfig&version=4.1.0

// Install FsConfig as a Cake Tool
#tool nuget:?package=FsConfig&version=4.1.0                

FsConfig

FsConfig 是一个用于从环境变量和 AppSettings 中读取配置数据并具有类型安全性的 F# 库

Nuget Build master

为何选择 FsConfig?

要理解 FsConfig,让我们从 FsTweet 应用程序的一个用例来看。

FsTweet 应用程序遵循 十二要素应用 指南来管理配置数据。在应用程序引导过程中,它从相应的环境变量中检索其十个配置参数。

open System

let main argv =

  let fsTweetConnString = 
   Environment.GetEnvironmentVariable  "FSTWEET_DB_CONN_STRING"

  let serverToken =
    Environment.GetEnvironmentVariable "FSTWEET_POSTMARK_SERVER_TOKEN"

  let senderEmailAddress =
    Environment.GetEnvironmentVariable "FSTWEET_SENDER_EMAIL_ADDRESS"

  let env = 
    Environment.GetEnvironmentVariable "FSTWEET_ENVIRONMENT"

  let streamConfig : GetStream.Config = {
      ApiKey = 
        Environment.GetEnvironmentVariable "FSTWEET_STREAM_KEY"
      ApiSecret = 
        Environment.GetEnvironmentVariable "FSTWEET_STREAM_SECRET"
      AppId = 
        Environment.GetEnvironmentVariable "FSTWEET_STREAM_APP_ID"
  }

  let serverKey = 
    Environment.GetEnvironmentVariable "FSTWEET_SERVER_KEY"

  let port = 
    Environment.GetEnvironmentVariable "PORT" |> uint16

  // ...

尽管代码片段完成这项工作,但仍有一些不足之处。

  1. 代码冗长。
  2. 没有错误处理来处理值缺失或错误值的情况。
  3. 显式类型转换

借助 FsConfig,我们可以通过将配置数据指定为 F# Record 类型来克服这些限制。

type StreamConfig = {
  Key : string
  Secret : string
  AppId : string
}

[<Convention("FSTWEET")>]
type Config = {

  DbConnString : string
  PostmarkServerToken : string
  SenderEmailAddress : string
  ServerKey : string
  Environment : string

  [<CustomName("PORT")>]
  Port : uint16
  Stream : StreamConfig
}

然后可以通过一次函数调用读取所有相关的环境变量,具有类型安全和错误处理!

let main argv =

  let config = 
    match EnvConfig.Get<Config>() with
    | Ok config -> config
    | Error error -> 
      match error with
      | NotFound envVarName -> 
        failwithf "Environment variable %s not found" envVarName
      | BadValue (envVarName, value) ->
        failwithf "Environment variable %s has invalid value %s" envVarName value
      | NotSupported msg -> 
        failwith msg

支持的数据类型

FsConfig 支持以下数据类型,并利用它们各自的 TryParse 函数进行类型转换。

  • Int16, Int32, Int64, UInt16, UInt32, UInt64
  • Byte, SByte
  • Single, Double, Decimal
  • Char, String
  • Bool
  • TimeSpanDateTimeOffsetDateTime
  • Guid
  • 枚举
  • 上述所有类型的list
  • 上述所有类型的option

选项类型

FsConfig 允许我们使用 option 类型指定可选配置参数。在之前的示例中,如果配置参数 Port 是可选的,我们可以定义如下

type Config = {
   ...
-  Port : uint16
+  Port : uint16 option
}

区分联合类型

FsConfig 支持只有情况单独的区分联合类型。

type Color =
| Red
| Green
| Blue 

type Config = {
  ConsoleColor : Color
}

通过此配置声明,FsConfig 读取环境变量 CONSOLE_COLOR 并填充类型为 ColorConsoleColor 字段。

还支持区分联合类型的列表!

列表类型

FsConfig 还支持 list 类型,并且它期望用逗号分隔的单独值。

例如,要获取多个端口,我们可以将配置定义如下

type Config = {
  Port : uint16 list
}

然后通过环境变量 PORT 使用值 8084,8085,8080

如果需要,可以通过使用 ListSeparator 属性更改列表的默认分隔符。

  [<Convention("MYENV")>]
  type CustomListSeparatorSampleConfig = {
    ProcessNames : string list
    [<ListSeparator(';')>]
    ProcessIds : uint16 list
    [<ListSeparator('|')>]
    PipedFlow : int list    
  }

通过此配置声明,FSConfig 可以从 App.settings 中读取以下条目。

  <add key="MYENVProcessNames" value="conhost.exe,gitter.exe"/>
  <add key="MYENVProcessIds" value="4700;15680"/>
  <add key="MYENVPipedFlow" value="4700|15680|-1" />

下述定义类似的结构将允许解析独立的列表。

  type IntListUsingSemiColonsConfig = {
    [<ListSeparator(';')>]
    IntListUp : int list
  }

例如,一个包含以下环境的环境

INT_LIST_UP=42;43;44 

记录类型

如初始示例所示,FsConfig 允许我们将类似的配置组合成一个记录类型。

type AwsConfig = {
  AccessKeyId : string
  DefaultRegion : string
  SecretAccessKey : string
}

type Config = {
  Aws : AwsConfig
}

通过此配置声明,FsConfig 读取环境变量 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_DEFAULT_REGION 并填充类型为 AwsConfigAws 字段。

默认值

如果您希望在没有字段值的情况下使用默认值,您可以使用 DefaultValue 属性。

type Config = {
  [<DefaultValue("8080")>]
  HttpServerPort : int16
  [<DefaultValue("Server=localhost;Port=5432;Database=FsTweet;User Id=postgres;Password=test;")>]
  DbConnectionString: string
}

环境变量名称约定 & 自定义

默认情况下,FsConfig 遵循下划线加首字母大写的约定,例如 UPPER_CASE,用于派生环境变量名称。

type Config = {
  ServerKey : string
}

使用此配置声明,FsConfig 读取环境变量 SERVER_KEY 并填充 ServerKey 字段

要指定环境变量中的自定义前缀,我们可以使用 Convention 属性。

[<Convention("FSTWEET")>]
type Config = {
  ServerKey : string
}

对于此配置声明,FsConfig 读取环境变量 FSTWEET_SERVER_KEY 并填充 ServerKey 字段。

我们还可以使用 Convention 属性的可选字段 Separator 来覆盖分隔符字符 _

[<Convention("FSTWEET", Separator="-")>]
type Config = {
  ServerKey : string
}

在这种情况下,FsConfig 将环境变量名称派生为 FSTWEET-SERVER-KEY

如果环境变量名称不遵循约定,我们可以在字段级别使用 CustomName 属性来覆盖环境变量名称。

type Config = {
  [<CustomName("MY_SERVER_KEY")>]
  ServerKey : string
}

在此,FsConfig 使用环境变量名称 MY_SERVER_KEY 来获取 ServerKey。

我们还可以通过在调用 Get 函数时传递一个高阶函数来简化(或控制)环境变量名称的生成。

open FsConfig

// Prefix -> string -> string
let lowerCaseConfigNameCanonicalizer (Prefix prefix) (name : string) = 
  let lowerCaseName = name.ToLowerInvariant()
  if String.IsNullOrEmpty prefix then 
    name.ToLowerInvariant()
  else
    sprintf "%s-%s" (prefix.ToLowerInvariant()) lowerCaseName


[<Convention("FSTWEET")>]
type Config = {
  ServerKey : string
}

let main argv =
  let config = 
    match EnvConfig.Get<Config> lowerCaseConfigNameCanonicalizer with
    | Ok config -> config
    | Error error -> failwithf "Error : %A" error

在此场景中,FsConfig 计算环境变量名称为 fstweet-server-key

获取单个环境变量

FsConfig 还支持通过显式指定环境变量名称来直接读取值

EnvConfig.Get<decimal> "MY_APP_INITIAL_BALANCE" // Result<decimal, ConfigParseError>

应用配置

FsConfig 支持适用于 DotNet Core 和非 DotNet Core 应用程序的应用程序配置。

DotNet Core 配置(从 V2.0.0 开始支持)

FsConfig 通过依赖于 IConfigurationRoot 来抽象配置提供程序。

let configurationRoot : IConfigurationRoot = // ...
let appConfig = new AppConfig(configurationRoot)

创建一个实例 appConfig(类型为 FsConfig 的 AppConfig)后,可以使用它按照以下方式读取配置值

// Reading Primitive
let result = 
  appConfig.Get<int> "processId" // Result<int, ConfigParseError>

// A Sample Record
type SampleConfig = {
  ProcessId : int
  ProcessName : string
}

// Reading a Record type
let result = 
  appConfig.Get<SampleConfig> () // Result<SampleConfig, ConfigParseError>

// A Sample Nested Record
type AwsConfig = {
  AccessKeyId : string
  DefaultRegion : string
  SecretAccessKey : string
}

type Config = {
  MagicNumber : int
  Aws : AwsConfig
}

// Reading a Nested Record type
let result = 
  appConfig.Get<Config> () // Result<Config, ConfigParseError>

以下是根据文件类型创建 configurationRoot 并使用 FsConfig 读取值的示例。

JSON
{
  "processId" : "123",
  "processName" : "FsConfig",
  "magicNumber" : 42,
  "aws" : {
    "accessKeyId" : "Id-123",
    "defaultRegion" : "us-east-1",
    "secretAccessKey" : "secret123"
  },
  "colors" : "Red,Green"
}

可以使用以下方法读取此 JSON 文件

// Requires NuGet package
// Microsoft.Extensions.Configuration.Json
let configurationRoot =  
  ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("settings.json").Build()

let appConfig = new AppConfig(configurationRoot)
let result = 
  appConfig.Get<Config> () // Result<Config, ConfigParseError>
XML
<Settings>
  <ProcessId>123</ProcessId>
  <ProcessName>FsConfig</ProcessName>
  <MagicNumber>42</MagicNumber>
  <Aws>
    <AccessKeyId>Id-123</AccessKeyId>
    <DefaultRegion>us-east-1</DefaultRegion>
    <SecretAccessKey>secret123</SecretAccessKey>
  </Aws>
  <Colors>Red,Green</Colors>
</Settings>

可以使用以下方法读取此 XML 文件

// Requires NuGet package
// Microsoft.Extensions.Configuration.Xml
let configurationRoot =  
  ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
    .AddXmlFile("settings.xml").Build()

let appConfig = new AppConfig(configurationRoot)
let result = 
  appConfig.Get<Config> () // Result<Config, ConfigParseError>
INI
ProcessId=123
ProcessName=FsConfig
MagicNumber=42
Colors=Red,Green

[Aws]
AccessKeyId=Id-123
DefaultRegion=us-east-1
SecretAccessKey=secret123

可以使用以下方法读取此 INI 文件

// Requires NuGet package
// Microsoft.Extensions.Configuration.Ini
let configurationRoot =  
  ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
    .AddIniFile("settings.ini").Build()

let appConfig = new AppConfig(configurationRoot)
let result = 
  appConfig.Get<Config> () // Result<Config, ConfigParseError>

appSettings(仅在 V0.0.6 或以下版本中受支持)

我们可以使用 AppConfig 类型而不是 EnvConfig 类型来读取 appSettings 的值。

FsConfig 使用字段的精确名称来导出 appSettings 键名,并默认不使用分隔符。

type AwsConfig = {
  AccessKeyId : string
  DefaultRegion : string
  SecretAccessKey : string
}

type Config = {
  Port : uint16
  Aws : AwsConfig
}

let main argv =
  let config = 
    match AppConfig.Get<Config>() with
    | Ok config -> config
    | Error error -> failwithf "Error : %A" error

上述代码片段会查找具有名称 PortAwsAccessKeyIdAwsDefaultRegionAwsSecretAccessKeyappSettings 值,并将其填充到相应的字段中。

我们对 EnvConfig 所做的所有自定义设置也适用于 AppConfig

FsConfig 的工作原理

如果您想了解 FsConfig 的工作原理及其内部结构,那么您可能对我的博客文章感兴趣,该文章深入探讨了 FsConfig 的初始实现,请参考泛型编程如此简单

反馈

我们所有人都需要愿意给我们反馈的人。这就是我们不断提高的方式——比尔·盖茨。

欢迎您的建议/反馈!

致谢

FsConfig 的灵感来源于 Kelsey Hightower 的 golang 库 envconfig

FsConfig 使用 Eirik TsarpalisTypeShape 库进行泛型编程。

维护者

产品 兼容的和额外的计算目标框架版本。
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.0 兼容。 netstandard2.1 已计算。
.NET Framework net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。
MonoAndroid monoandroid已计算。
MonoMac monomac已计算。
MonoTouch monotouch已计算。
Tizen tizen40已计算。 tizen60已计算。
Xamarin.iOS xamarinios已计算。
Xamarin.Mac xamarinmac已计算。
Xamarin.TVOS xamarintvos已计算。
Xamarin.WatchOS xamarinwatchos已计算。
兼容的目标框架
包含的目标框架(在包中)
了解更多关于 目标框架.NET标准

NuGet包

此包没有被任何NuGet包使用。

GitHub存储库

此包没有被任何流行的GitHub存储库使用。

版本 下载 最后更新
4.1.0 9,157 10/24/2023
4.0.0 11,235 6/18/2023
3.1.0 65,858 7/25/2021
3.0.0 24,580 1/30/2021
2.1.6 20,119 4/26/2020
2.1.5 9,521 11/11/2019
2.1.4 1,145 10/18/2019
2.1.3 3,889 6/9/2019
2.1.2 719 5/24/2019
2.0.2 2,219 8/12/2018
2.0.1 908 7/27/2018
2.0.0 1,261 6/26/2018
2.0.0-beta1 783 6/6/2018
1.2.1 1,261 5/29/2018
1.1.2 1,004 5/29/2018
1.1.1 1,034 5/10/2018
1.1.0 930 5/7/2018
1.0.0 1,772 4/28/2018
1.0.0-beta1 954 4/28/2018
0.0.6 1,185 4/27/2018
0.0.5 1,007 2/23/2018
0.0.4 979 2/5/2018
0.0.3 1,023 2/3/2018
0.0.2 975 2/3/2018
0.0.1 1,035 2/1/2018

## [4.1.0] - 2023-10-24

[4.1.0]: https://github.com/demystifyfp/FsConfig/compare/v4.0.0...v4.1.0

### 添加

- 由[queil](https://github.com/queil)提供的可选子部分支持