函数App、实体框架、.Net 6和环境变量

e4yzc0pl  于 2023-04-22  发布在  .NET
关注(0)|答案(2)|浏览(131)

将使用Entity Framework(代码优先)的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中,我可以很容易地从“值”部分检索变量,但在更新到.Net 6之后,当运行dotnet ef命令时,它们总是空的。

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

1hdlvixo1#

这里有一个我刚刚在v4 / .NET 7 /隔离的Azure函数应用程序中工作的解决方案。
基本上,如果在.csprojCopyToOutputDirectory中配置了local.settings.json,我们可以手动绑定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);
    }
}

我仍然希望看到一个更官方的修复。This GitHub Issue是相关的。

pgvzfuti

pgvzfuti2#

我对这个问题有不同的看法。我完全不介意连接字符串为空,因为在运行dotnet ef命令时不需要那个连接字符串。
我的推理从这个命令开始:

dotnet ef migrations list --no-connect

这里我只想看到我已经添加的迁移,但我不关心它们是否已经应用,--no-connect参数明确指出我不想连接到DB =〉我不需要连接字符串!
然而,前面的命令将失败,并显示The string argument 'connectionString' cannot be empty
dotnet ef命令将触发DI管道,以便能够“发现”DbContext和实体的配置:如果启动项目使用ASP.NET Core Web主机或.NET Core通用主机,则工具将尝试从应用程序的服务提供程序获取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以及以下事实:
-- token指示dotnet ef将后面的所有内容都视为参数,而不是试图将它们解析为选项。dotnet ef未使用的任何额外参数都将转发给应用
所以我成功地运行了dotnet ef命令:

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

如果我真的想使用某个dotnet ef命令访问数据库,我会将连接字符串作为参数传递给带有--connection参数的命令。

相关问题