Function App、Entity Framework、.Net 6和环境变量

3

在将使用Entity Framework(Code First)的Azure Functions应用程序更新为.Net 6时,来自local.settings.json的任何值都无法找到。此问题出现在运行“ef”命令时,但在调试或运行应用程序时不会出现。

Azure Functions应用程序配置为在隔离模式下运行,并使用Environment.GetEnvironmentVariable方法从设置文件中检索值。而且当调试或运行应用程序时,这个方法非常有效。

// Simplified code example
public static async Task Main(string[] args)
{
  var builder = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(s =>
    {
      var connectionString = Environment.GetEnvironmentVariable("ConnectionString");
      // connectionString is null here only when running dotnet ef command
    });
  builder.RunAsync();
}

但是,如果我从终端运行任何 ef 命令(比如 dotnet ef database update),所有的值都是 null,因此似乎在运行这些命令时并没有考虑 local.settings.json 文件。

当从 .Net 5 运行时,这个方法很好用。当然,EntityFrameworkCore NuGet 包也已经更新以支持新的 .Net 版本。


更新

为了澄清,local.settings.json 文件是简单的,结构化的方式。在 .Net 5 中,我可以轻松地从“Values”部分检索变量,但在升级到 .Net 6 后,在运行 dotnet ef 命令时,它们总是为空。

{
    "IsEncrypted": false,
    "Values": {
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "ConnectionString": "[...]"
    },
    "Host": {
        "CORS": "*",
        "CORSCredentials": false
    }
}

请提供您的 local.settings.json 文件,以便查看您是如何在值或连接字符串下添加这些值的。 - anon
@HariKrishnaRajoli-MT 我更新了问题,现在看起来是这样的。 - einord
请提供您尝试访问“local.settings.json”的逻辑! - anon
@HariKrishnaRajoli-MT 我已经更新了问题并附上了示例。 - einord
1
我得到了同样的结果。ef正在查看的环境变量是Windows中存储的变量,而不是local.settings.json中存储的变量。 - zeiddev
2个回答

0

这里有一个我刚在 v4 / .NET 7 / 隔离的 Azure 函数应用中解决问题的 解决方法

基本上,如果在 .csproj 中配置了 local.settings.jsonCopyToOutputDirectory,我们可以手动绑定 Configuration 并从 values 部分中提取值。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;

namespace MyApp;

public class MyAppDbContextDesignTimeFactory : IDesignTimeDbContextFactory<MyAppDbContext>
{
    public MyAppDbContext CreateDbContext(string[] args)
    {
        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("local.settings.json")
            .Build();
        const string connectionStringKey = "MyAppDbConnectionString";
        var connectionString = Environment.GetEnvironmentVariable(connectionStringKey) ??
                               configuration.GetSection("values").GetValue<string>(connectionStringKey) ??
                               throw new InvalidOperationException($"Missing {connectionStringKey}");
        var optionsBuilder = new DbContextOptionsBuilder<MyAppDbContext>();
        optionsBuilder.UseSqlServer(connectionString);
        return new MyAppDbContext(optionsBuilder.Options);
    }
}

我仍然希望看到更官方的解决方法。这个 GitHub 问题 与此相关。


0

我对这个问题有不同的看法。如果运行 dotnet ef 命令时不需要该连接字符串,那么该连接字符串为空是完全可以接受的。

我的理由始于以下命令:

dotnet ef migrations list --no-connect

这里我只想查看我已经添加的迁移,但是我不关心它们是否已经被应用,--no-connect参数明确表示我不想连接到数据库 => 我不需要连接字符串!

然而,前面的命令会失败并显示The string argument 'connectionString' cannot be empty

dotnet ef 命令将触发 DI 管道以便能够“发现” DbContext 和实体的配置:“如果您的启动项目使用 ASP.NET Core Web Host 或 .NET Core Generic Host,则工具会尝试从应用程序的服务提供程序中获取 DbContext 对象。

理想情况下,因此创建的 DbContext 应该以与运行时相似的方式进行配置。

连接字符串本身为空并不是问题,但当像 myOpBuilder.UseSqlServer(someNullString) 这样的方法调用出现时,就会抱怨 The string argument 'connectionString' cannot be empty

因此,我采取的解决方案是:

var dbConStr = Configuration.GetConnectionString("DataBase");

services.AddDbContext<MyDbContext>(o =>
{
   var isMigration = Configuration.GetValue<bool>("mig");

   if (isMigration)
   {
       o.UseSqlServer();
   }
   else
   {
        o.UseSqlServer(dbConStr);
   }
});

当使用 dotnet ef 命令时,我利用了 CommandLineConfigurationProvider事实

-- 标记指示 dotnet ef 将其后面的所有内容都视为参数而不尝试将其解析为选项。 任何 dotnet ef 没有使用的额外参数将被转发到应用程序

因此,我成功地运行了我的 dotnet ef 命令:

dotnet ef migrations list --no-connect -- mig=true

如果我真的想要使用一些 dotnet ef 命令来访问数据库,我会将连接字符串作为参数传递给命令,并使用 --connection 参数

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接