【问题标题】:TimerTrigger does not inject EF Database ContextTimerTrigger 不注入 EF 数据库上下文
【发布时间】:2021-03-22 08:15:39
【问题描述】:

我有一个使用实体框架 (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; }
    }
}

一个启动类

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 个小时的不同事情:

  • 不出所料地访问 db 而不注入会出现一个空属性,这没有什么魔力。
  • MyDbContext 添加到Run 函数失败,因为它无法注入public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, MyDbContext db)
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.).
  • 与前面的操作相同,但通过在方法签名中添加 IServiceProvider services 会导致类似的错误消息,如果无法注入,添加行 db = services.GetRequiredService&lt;MyDbContext&gt;(); 是无关紧要的
  • 例如,某些变量似乎可以在此范围内注入 ExecutionContext,但我似乎无法在该对象上使用任何东西。

有没有办法:

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

更新:

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

查看这篇博文了解更多信息: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);
        }

【问题讨论】:

  • 尝试使用非静态 Run 方法。许多示例使用静态方法,如果您没有依赖项并且该方法是纯的,则可以推荐该方法。 (因为函数方法应该力求纯粹)有关 TimerTriggers /w DI 的示例,请参见 marcroussy.com/2019/05/31/…
  • @StevePy - 有趣!我所做的阅读说计时器函数需要一个静态运行函数。这是一个可以尝试的新途径。我会马上解决的。
  • @StevePy - 非常感谢。我很高兴地报告它按预期工作。看起来我读过的上一篇文章表明 RUN 方法必须是静态的 timertrigger 已过时。显然它有效。如果您在下面写下您的评论作为答案,我会很乐意为您接受。
  • 别担心,这是一个有趣的问题,需要仔细检查一下。 .随着事情的变化,在线文档源可能有点陈旧,有时您需要适用于特定版本的信息,而最受欢迎的内容是更新或旧版本。 :) 如果内容没有清楚地标明版本甚至日期,那就更糟了。

标签: entity-framework dependency-injection azure-functions timer-trigger


【解决方案1】:

尝试使用非静态 Run 方法。许多示例使用静态方法,如果您没有依赖项并且该方法是纯的,则可以推荐该方法。 (因为函数方法应该力求纯粹)有关 TimerTriggers /w DI 的示例,请参见 https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/

【讨论】:

    【解决方案2】:

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

    查看这篇博文了解更多信息: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);
            }
    

    请注意,为了在应得的情况下给予信任,如果史蒂夫做出回应,我会将已接受的答案切换为他的答案,但我现在将其标记为已回答,以确保该问题具有可接受的答案。

    【讨论】:

      猜你喜欢
      • 2021-12-28
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 2019-06-21
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多