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 构造函数的主要内容,如果未能解决你的问题,请参考以下文章