Azure 函数使用了错误的 DbContext 构造函数

Posted

技术标签:

【中文标题】Azure 函数使用了错误的 DbContext 构造函数【英文标题】:Azure Function Uses Wrong DbContext Constructor 【发布时间】:2020-01-04 01:32:25 【问题描述】:

我有一个现有的 EF Core 2.2 DbContext 可以在 ASPNET Core 应用程序和 LinqPad 中正常工作。现在我正在尝试将它添加到 Azure 函数中。在 ASPNET 和 Azure 函数中,我都在使用依赖注入。

DbContext 类具有三个构造函数 - 一个为空,一个接受连接字符串,另一个接受 DbOptionsBuilder 实例。 ASPNET Core 应用程序似乎调用了采用 DbOptionsBuilder 实例的应用程序,而 LinqPad 使用了采用连接字符串的应用程序。正如我所说,这两个都可以正常工作。

Azure 函数应用尝试使用接受字符串的函数应用,但它传递的是 null 而不是值。这会导致稍后出现错误,提示尚未配置提供程序。

我可以强制函数应用使用DbOptionsBuilder 构造函数,方法是删除接受字符串的构造函数。当我这样做时,功能应用程序工作正常。但是,如果我这样做了,我将无法再在 LinqPad 中使用上下文。

我的问题是,首先,如何让 Azure 函数调用适当的构造函数而不删除其他构造函数?其次,也是不太重要的,为什么 ASPNET 运行时和 Azure 函数运行时之间的行为不同?

编辑 我此时仅在本地运行 AZ 函数,因此它正在从“local.settings.json”文件中读取连接字符串。这部分工作正常。

这里是函数项目的Startup.Configure方法。

public class Startup : FunctionsStartup

    /// <summary>
    /// This method gets called by the runtime. Use this method to add services to the DI container.
    /// </summary>
    /// <param name="builder">The function host builder</param>
    public override void Configure(IFunctionsHostBuilder builder)
    
        // Add database context

        string env = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
        string connectionString = Environment.GetEnvironmentVariable($"ConnectionStrings:env");

        builder.Services.AddDbContext<FullContext>(x => x.UseSqlServer(connectionString), ServiceLifetime.Transient);
    

正如我所说,它正在读取连接字符串并似乎将其传递给 AddDbContext 方法。但是某处出了点问题。

编辑 2 这是我的DbContext 子类中的三个构造函数。没什么特别的。还包括OnConfiguring 方法。

    public FullContext()  

    public FullContext(string connectionString)
    
        ConnectionString = connectionString;
    

    public FullContext(DbContextOptions<FullContext> options) : base(options)  

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    
        if (ConnectionString != null)
            optionsBuilder.UseSqlServer(ConnectionString);

        base.OnConfiguring(optionsBuilder);
    

编辑 3 在查看了@Jack Jia 建议的链接后,我尝试了以下操作。

首先,我创建自己的DbContextOptionsBuilder 实例并指定提供程序和连接字符串。

    var options = new DbContextOptionsBuilder<FullContext>();
    options.UseSqlServer(connectionString);

然后我尝试强制 DI 服务使用这些选项。但是,使用 AddDbContext 方法时会失败 - 它仍然尝试使用空字符串作为参数调用错误的构造函数。

换句话说,这失败了:

builder.Services.AddDbContext<FullContext>(x => new FullContext(options.Options), ServiceLifetime.Transient);

但这似乎有效:

builder.Services.AddTransient<FullContext>(x => new FullContext(options.Options));

假设我正确理解了文档,这两个调用都应该强制 DI 服务使用带有 DbContextOptions 参数的构造函数。但这似乎并非如此。

【问题讨论】:

取决于你如何注册上下文(你没有显示) 如果没有minimal reproducible example 明确指出已完成的工作,就很难重现问题,从而更好地理解所询问的内容。 【参考方案1】:

连接字符串值存储在哪里? 我会检查来源。开箱即用的 asp.net 核心有一个为注入配置的 application.settings.json 文件。 AZ 函数不这样做。 如果您使用的是 application.settings.json,那么您必须将其配置为从该文件加载设置。

这里是如何在 DI 中加载配置文件的示例,它允许您对内容具有与 asp.net core 中类似的访问权限:

var config = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory)
                .AddJsonFile("application.settings.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();
builder.Services.AddSingleton<IConfiguration>(config);

并在 Configure 方法中获取一个值: string SqlConnectionString = config.GetConnectionString("SqlConnectionString");

这是在public override void Configure(IFunctionsHostBuilder builder) 中完成的。 Here is how to use DI in Azure Functions.

我能想到的另一种可能性是 Azure Key Vault 或环境变量。

【讨论】:

谢谢,但问题与获取连接字符串无关。它可以从 local.settting.json 文件中读取它。问题是 .Net 使用了错误的构造函数。 在 DbContext 中,您的构造函数看起来如何? DbContext 构造器可​​以覆盖 DI 过程。 public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options) : base(options) //do whatever you need here, like Database.EnsureCreated(); 【参考方案2】:

您可以参考:Service registration methods

如果有多个构造函数,可以指定如下:

AddLIFETIME<SERVICE>(sp => new IMPLEMENTATION)

例如:

// Constructor1
builder.Services.AddScoped<IMyDep>(sp => new MyDep());

// Constructor2
builder.Services.AddScoped<IMyDep>(sp => new MyDep("A string!"));

// Constructor3
builder.Services.AddScoped<IClass1, Class1>();
builder.Services.AddScoped<IMyDep>(sp =>

    IClass1 class1 = sp.GetRequiredService<IClass1>();
    //class1.doSomething(...);
    return new MyDep(class1);
);

因此,您无需更改 DbContext 类,只需在不同的应用程序中专门使用不同的构造函数即可。

【讨论】:

以上是关于Azure 函数使用了错误的 DbContext 构造函数的主要内容,如果未能解决你的问题,请参考以下文章

使用 DBContext 将 SQL Server 数据库与时间触发的 Azure 函数连接起来

使用身份验证托管的 Blazor Wasm 上出现 Azure 500 错误

Entity Framework DbContext 使用错误的构造函数初始化

通过 HTTP 调用 Azure Function 150 次导致异常

ASP.NET vNext EF7 dbContext 问题

Azure 函数状态码 500 内部服务器错误