.NET 配置体系结构

作者:微信公众号:【架构师老卢】
8-28 16:37
7

概述:如今,.NET 应用程序开箱即用,具有一组强大的配置源。新搭建的 ASP .NET 应用程序从 JSON 文件 () 、环境变量和命令行参数中读取配置。尽管如此,部分正因为如此,维护干净的配置架构成为一项挑战。让我们来了解一个特别常见的架构难题,并尝试找到解决方案。appsettings.json数据库和 AppSettings本文使用 Confitecture 作为项目名称:配置 + 架构。假设我们正在开发一个 ASP .NET 应用程序。我们刚刚通过 .现在我们想把它连接到一个数据库,所以我们需要把我们的连接字符串放在某个地方。显然,我们不能直接用代码来表达,因为在开发和生产中会有不同的数

如今,.NET 应用程序开箱即用,具有一组强大的配置源。新搭建的 ASP .NET 应用程序从 JSON 文件 () 、环境变量和命令行参数中读取配置。尽管如此,部分正因为如此,维护干净的配置架构成为一项挑战。让我们来了解一个特别常见的架构难题,并尝试找到解决方案。appsettings.json

数据库和 AppSettings

本文使用 Confitecture 作为项目名称:配置 + 架构。

假设我们正在开发一个 ASP .NET 应用程序。我们刚刚通过 .现在我们想把它连接到一个数据库,所以我们需要把我们的连接字符串放在某个地方。显然,我们不能直接用代码来表达,因为在开发和生产中会有不同的数据库。简要了解一下文件夹结构可以提示这一点,并且可以轻松处理该用例。确实,如果我们再加上dotnet new web --name Confitectureappsettings.jsonappsettings.Development.jsonappsettings.json

{  
    "ConnectionStrings": {  
        "Db" : "ProductionDbConnectionString"  
    },  
    // ...  
}

和 在 :appsettings.Development.json

{  
    "ConnectionStrings": {  
        "Db" : "DevelopmentDbConnectionString"  
    },  
    // ...  
}

,然后使用该连接字符串:

var dbConnectionString = app.Configuration.GetConnectionString("Db");  
app.Logger.LogInformation("Db Connection string: {dbConnectionString}", dbConnectionString);

我们将能够根据我们运行的环境获取不同的连接字符串:

dotnet run --environment=Development # logs `Db Connection string: DevelopmentDbConnectionString`dotnet run --environment=Production # logs `Db Connection string: ProductionDbConnectionString\`

但是,这种方法存在一些问题:

  1. 在代码中存储连接字符串并不安全,因为任何开发人员都可以轻松访问生产数据库密码。
  2. 完全有可能有大量的环境。事实上,在我的实践中,我见过仓库在每个项目中都有 、 文件。当然,这导致了很多重复和一般的维护混乱。appsettings.Local.jsonappsettings.Development.jsonappsettings.Local.jsonappsettings.QA1.jsonappsettings.QA2.jsonappsettings.Staging.jsonappsettings.json
  3. 由于既是 production 又是 default 配置源,因此忘记覆盖某些内容可能会导致在调试期间在 production 上连接并可能修改某些内容。appsetting.json
  4. 模糊配置新环境时需要指定的配置值列表。有时会导致假阳性配置,如第 #3 点所示。

那么,我们找到更好的东西怎么样?

连接的环境变量

好吧,我们还剩下两个很棒的配置源:Environment Variable 和 Command Line arguments。从某种意义上说,它们都提供了非常相似的体验:它们是外部化的,并且几乎每个 CI 系统都原生支持它们。环境变量的主要论点可能是,当它们的数量增加时,它们会更容易维护。让我们对如何在应用程序中使用 Environment Variable 进行原型设计。

为了 “保持真实” ,现在让我们实际使用我们的连接字符串来连接到数据库。首先,让我们使用 Postgres 添加 EF Core:

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL

并更新 our 以连接到数据库:Program.cs

using Microsoft.EntityFrameworkCore;  
  
var builder = WebApplication.CreateBuilder(args);  
builder.Services.AddDbContext<DbContext>(  
    o => o.UseNpgsql(builder.Configuration.GetConnectionString("Db"))  
);  
var app = builder.Build();  
app.MapGet("/", async (DbContext context) => {  
    await context.Database.OpenConnectionAsync();  
    return "Connected!";  
});  
app.Run();

也许,一个好的存储库必须做的第一件事是提供一种在本地部署它的方法。在我们的例子中,这意味着提供一种简单的方法来部署 PostgreSQL 数据库,并连接我们的应用程序。如今,docker compose 是实现这一目标的最流行和最简单的方法。首先,我们需要一个 :Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build  
EXPOSE 8080  
WORKDIR /app  
  
COPY . .  
RUN dotnet publish "Confitecture.csproj" -c Release -o /app/publish  
WORKDIR /app/publish  
ENTRYPOINT dotnet Confitecture.dll

然后是 :compose.yml

name: confitecture

services:
  app:
    image: confitecture
    ports:
      - "53593:8080"
    build: .
    profiles: [ "full" ]
    environment:
      - CONNECTIONSTRINGS__DB=Host=db;Port=5432;Username=postgres;Password=postgres
      - ASPNETCORE_ENVIRONMENT=Development
  db:
    image: postgres
    environment:
      - POSTGRES_PASSWORD=postgres
    ports:
      - "5432:5432"

设置完成后,如果我们要部署我们的服务,请等待 1 秒钟并调用我们的终端节点:

docker compose up -d && sleep 1 && curl localhost:53593

我们会得到回复的!请注意 .这就是我们将环境变量从 docker 传播到 .NET 应用程序的方式。Connected!- CONNECTIONSTRINGS__DB=Host=db;Port=5432;Username=postgres;Password=postgres

💪 很酷的是,我们不会泄露任何特定于 docker 的详细信息(如 docker 内部网络域名)。db

使用 LaunchSettings 引导开发人员

当我们从 AppSettings 迁移到 Environment Variables 时,我们失去了一件事,即开发人员只需使用 即可运行应用程序进行调试的能力。让我们把这个拿回来吧!我们需要做的第一件事是为开发人员提供一种简单的方法来部署基础设施服务。这可以通过 docker compose service profiles 来实现。如果我们将配置文件添加到应用服务,如下所示:dotnet runfull

name: confitecture

services:
  app:
    # ...
    profiles: [ "full" ]
      
  db:
    # ...

仅当在 docker compose 命令中指定了配置文件时,该服务才会运行。并且 profile-less 将一直运行。让我们先 kill all the services 来检查一下:appdb

docker compose --profile full down

然后只启动服务。db

docker compose up -d

现在 PostgreSQL 部署在 上后,我们需要做的就是找到一个可以放置连接的地方,以便仅在开发人员在本地运行应用程序(例如用于调试)时使用它。 正是达到这个目的。因此,如果我们将文件的内容更新为:localhostlocalhostlaunchSettings.json

{  
  "$schema": "http://json.schemastore.org/launchsettings.json",  
  "profiles": {  
    "Local": {  
      "commandName": "Project",  
      "dotnetRunMessages": true,  
      "launchBrowser": true,  
      "applicationUrl": "http://localhost:53593",  
      "environmentVariables": {  
        "ASPNETCORE_ENVIRONMENT": "Development",  
        "CONNECTIONSTRINGS__DB": "Host=localhost;Port=5432;Username=postgres;Password=postgres"  
      },  
    }  
  }  
}

开发人员将能够只发送命令(它将使用唯一现有的启动配置文件)来启动并运行我们的应用程序。在大多数情况下,您可能会从 而不是 docker 文件开始开发。当您让配置文件正常工作时,它还将识别您必须在 docker-compose 文件中指定哪些环境变量。dotnet runlaunchSettingslaunchSettings

☝️ 在我看来,使用 over ( 也提供)的主要论点是环境变量有一些特性。您可能已经注意到 docker compose 中变量名称中的双下划线 ()。因此 ,提供了捕获可能的配置错误的最快方法。

阅读排行