TimerTrigger 不注入 EF 数据库上下文

TimerTrigger does not inject EF Database Context

我有一个使用 Entity Framework (3.0.11) 的 Azure 函数 (v3)。

我正在尝试 运行 TimerTrigger 上的代码,但是在计时器触发器中注入数据库似乎不起作用。

这里有一些(快速匿名化的)代码示例。

CSPROJ

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="AzureFunctions.Extensions.DependencyInjection" Version="1.1.3" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

模型和 DBContext

namespace DataImport
{
    public class Sample
    {
        public int SampleID { get; set; }
        public string SampleField { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
        public virtual DbSet<Sample> MyRecords { get; set; }
    }
}

一家初创公司class

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;

[assembly: FunctionsStartup(typeof(DataImport.Startup))]
namespace DataImport
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            string con = builder.GetContext().Configuration.GetSection("ConnectionStrings:DefaultConnection").Value.ToString();
            builder.Services.AddDbContext<MyDbContext>(config => config.UseSqlServer(con));
        }
    }
}

一个Program.cs

using System;
using System.Linq;
using System.Threading.Tasks;
using AzureFunctions.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace DataImport
{


    public class Program
    {
        private readonly MyDbContext db;
        public Program(MyDbContext database)
        {
            db = database;
        }

        [FunctionName("SampleFunction_works")]
        public async Task<IActionResult> HttpRun([HttpTrigger(AuthorizationLevel.Anonymous, "GET")] HttpRequest req, ILogger log, ExecutionContext context)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
            var foo = db.MyRecords.Where(c => c.SampleField == "000").FirstOrDefault();
            await db.MyRecords.AddAsync(new Sample());
            log.LogInformation(foo.SampleField);
            return new OkObjectResult(foo);
        }

        [FunctionName("SampleFunction_no_work")]
        public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, ExecutionContext context)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
            // tried dozens of things here, nothing works sofar.
            // injecting IServiceProvider fails,
            // what other ways to solve this?
            // could a timer trigger perhaps make an HTTP call to the HttpRun function above? 
        }
    }
}

当 运行使用数据库连接连接 SampleFunction_works 时,我们看到函数调用的结果是成功的。注入在 HTTP 触发器的上下文中工作。然而,在定时器触发器上,这不起作用。

此时我已经尝试了 8 个小时的不同事物:

Microsoft.Azure.WebJobs.Host: Error indexing method 'SampleFunction_no_work'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'db' to type MyDbContext. Make sure the parameter Type is supported by the binding. If you're using binding MyDbContext(e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

有没有办法:

  1. 用数据库注入定时器触发器?
  2. 使用计时器触发器调用位于同一函数内的 HTTP 触发器?
  3. 任何其他允许我在 timertrigger 上下文中访问 EF 数据库的解决方案?

更新:

@StevePy 下面的评论是正确的。您可以使 timertrigger 的 运行 方法成为非静态的,并利用注入的力量。我之前读到过这是不可能的,但信息似乎已经过时了。

查看此博客 post 了解更多信息:https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/

或者在本地将此示例代码抓取到 运行:

        [FunctionName("MY_FANCY_FUCNTION")]
        public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
        {
            ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
            // note that the key part of this DOES log out as NOT NULL
            // which is what we want.
            return;
            await Main(ilog, context);
        }

@StevePy 在我的问题下方的评论是正确的。您可以使 timertrigger 的 运行 方法成为非静态的,并利用注入的力量。我之前读到过这不可能,但信息似乎已过时。

查看此博客 post 了解更多信息:https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/

或将此示例代码抓取到本地 运行:

        [FunctionName("MY_FANCY_FUCNTION")]
        public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
        {
            ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
            // note that the key part of this DOES log out as NOT NULL
            // which is what we want.
            return;
            await Main(ilog, context);
        }

请注意,为了在到期时给予信用,我会将接受的答案切换到史蒂夫的回答,但我现在标记为已回答以确保问题有一个接受的答案.

尝试使用非静态 运行 方法。许多示例使用静态方法,在您没有依赖项且方法是纯方法时,可以推荐使用该方法。 (因为函数式方法应该努力做到纯粹)有关 TimerTriggers /w DI 的示例,请参阅 https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/