使用带有 .NET 5 的 Azure Functions 将不同的连接字符串传递给 IDesignTimeDbContextFactory(独立)

Pass different connection strings to IDesignTimeDbContextFactory using Azure Functions with .NET 5 (Isolated)

我在 Visual Studio 版本 16.10.0 中使用带有 .NET 5(独立)和 Http 触发器的模板 Azure Functions 创建了一个新的 FunctionApp。

按照设计时 DbContext 创建指南,我似乎只能使用 IDesignTimeDbContextFactory 而不是 application services。我已经为使用 HostBuilder.

创建了一个单独的问题

https://docs.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=vs

Microsoft.EntityFrameworkCore.Tools - Azure Functions with .NET 5 (Isolated) Design-time DbContext Creation with HostBuilder

Environment.GetEnvironmentVariable 获取连接字符串在运行时不是问题:

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {

        var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:DefaultConnection");
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        optionsBuilder.UseSqlServer(connectionString);

        return new ApplicationDbContext(optionsBuilder.Options);
    }
}

local.settings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=FunctionApp1.Test;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
  }
}

函数 1cs:

var applicationDbContextFactory = new ApplicationDbContextFactory();
using var context = applicationDbContextFactory.CreateDbContext(null);

但是在 Package Manager Console 中使用 Microsoft.EntityFrameworkCore.Tools Add-Migration InitialCreate 我得到以下错误:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')

这是预期的,因为当 运行 通过 Package Manager Console 时环境变量不存在。我也不能使用这样的代码:

$ConnectionStrings.DefaultConnection="Server=tcp:mySqlServerStuffxxx"
Add-Migration InitialCreate

会导致以下错误:

The property 'DefaultConnection' cannot be found on this object. Verify that the property exists and can be set.

我可以像这样加载连接字符串,但在生产中我想要一个不同的值。使用默认部署和签入的 appsettings.json 效果更好。

IConfigurationRoot configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("local.settings.json")
    .Build();

var connectionString = configuration.GetConnectionString("DefaultConnection");

然后我尝试使用 MS 文档中提到的 -Args

https://docs.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=vs#args

var connectionString = args[0];

有了这个我可以添加一个新的迁移,它看起来不错。

Add-Migration InitialCreate -Args 'Server=(localdb)\mssqllocaldb;Database=FunctionApp1.Test;Trusted_Connection=True;MultipleActiveResultSets=true'

然而,当 运行 Update-Database 它失败并出现以下错误:

Microsoft.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SNI_PN11, error: 50 - Local Database Runtime error occurred. Specified LocalDB instance name is invalid.

Update-Database -Args 'Server=(localdb)\mssqllocaldb;Database=FunctionApp1.Test;Trusted_Connection=True;MultipleActiveResultSets=true'

如果我硬编码完全相同的值或从 ConfigurationBuilder 中获取值,则一切都适用于这些设置。

var connectionString = "Server=(localdb)\mssqllocaldb;Database=FunctionApp1.Test;Trusted_Connection=True;MultipleActiveResultSets=true";

将不同的连接字符串传递给在运行时和 Package Manager Console 都有效的 IDesignTimeDbContextFactory 的最佳方法是什么?

考虑到 ConfigurationBuilder 的解决方案无需复制连接字符串即可工作,我们决定使用该解决方案,然后仅将 ApplicationDbContextFactory 用于设计,绝不用于代码。

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("local.settings.json")
            .Build();

        var connectionString = configuration.GetConnectionString("DefaultConnection");
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        optionsBuilder.UseSqlServer(connectionString);

        return new ApplicationDbContext(optionsBuilder.Options);
    }
}

然后当 Entity Framework 被移动到它自己的 class 库时出现另一个错误。

Your target project 'FunctionApp1.FunctionApp' doesn't match your migrations assembly 'FunctionApp1.EntityFramework'. Either change your target project or change your migrations assembly. Change your migrations assembly by using DbContextOptionsBuilder. E.g. options.UseSqlServer(connection, b => b.MigrationsAssembly("FunctionApp1.FunctionApp")). By default, the migrations assembly is the assembly containing the DbContext. Change your target project to the migrations project by using the Package Manager Console's Default project drop-down list, or by executing "dotnet ef" from the directory containing the migrations project.

通过将 PMC 中的默认项目设置为 Entity Framework 项目并将启动项目设置为包含 local.settings.json.

的项目来解决此问题