在 Azure Function 启动时进行 EF Core 迁移

9
根据https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection,服务提供商应在启动完成后才使用。如果我尝试获取已注册的服务,它将失败。
示例:
[assembly: FunctionsStartup(typeof(Startup))]

namespace Fx {
    public sealed class Startup : FunctionsStartup {
        public override void Configure(IFunctionsHostBuilder builder) {
            var configurationBuilder = new ConfigurationBuilder();
            configurationBuilder.AddEnvironmentVariables();

            var configuration = configurationBuilder.Build();

            builder.Services.AddInfrastructure(configuration);
            builder.Services.AddApplication();

            var serviceProvider = builder.Services.BuildServiceProvider();
            DependencyInjection.AddDatabase(serviceProvider).GetAwaiter().GetResult();
        }
    }
}

    public static class DependencyInjection {
        public static async Task AddDatabase(IServiceProvider services) {
            using var scope = services.CreateScope();

            var serviceProvider = scope.ServiceProvider;

            var context = serviceProvider.GetRequiredService<ApplicationDbContext>();
            //Error generated here
            if (context.Database.IsSqlServer()) {
                await context.Database.MigrateAsync();
            }
            await ApplicationDbContextSeed.SeedSamplePersonnelDataAsync(context);
        }

        public static IServiceCollection AddInfrastructure(
            this IServiceCollection services,
            IConfiguration configuration) {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
                    b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));

            services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

            return services;
        }
    }

这会产生以下错误。
Microsoft.EntityFrameworkCore: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.

在启动期间是否有迁移和种子数据的好选择?


你是否参考了 https://markheath.net/post/ef-core-di-azure-functions? - Jim Xu
我有这个。这里谈到了如何访问数据库上下文,但没有介绍如何进行迁移或种子数据填充。 - greven
请问您是否已经解决了这个问题,如果是的话,能否分享一下您的答案?我也遇到了同样的问题。 - Paramesh Korrakuti
请阅读注意事项:https://learn.microsoft.com/zh-cn/azure/azure-functions/functions-dotnet-dependency-injection - SKARVA Bodavula
3个回答

10
我发现在启动后运行代码最简单的方法是通过使用WebJobsStartupAttribute(实际上FunctionsStartupAttribute也继承自该属性)注册一个自定义的IWebJobsStartup。在WebJobsStartup类中,您需要使用AddExtension注册您的扩展程序,从而能够使用依赖注入(seed your database)。我的代码:
[assembly: WebJobsStartup(typeof(DbInitializationService), "DbSeeder")]

namespace Our.Database.Seeder
{
    public class DbInitializationService : IWebJobsStartup
    {
        public void Configure(IWebJobsBuilder builder)
        {
            builder.AddExtension<DbSeedConfigProvider>();
        }
    }

    [Extension("DbSeed")]
    internal class DbSeedConfigProvider : IExtensionConfigProvider
    {
        private readonly IServiceScopeFactory _scopeFactory;

        public DbSeedConfigProvider(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }

        public void Initialize(ExtensionConfigContext context)
        {
            using var scope = _scopeFactory.CreateScope();
            var dbContext = scope.ServiceProvider.GetService<YourDbContext>();

            dbContext.Database.EnsureCreated();
            // Further DB seeding, etc.
        }
    }
}

你是如何创建YourDbContext的,以及在哪里创建的?我在正常的FunctionsStartup中使用.AddDbContext()来创建它,但是在执行scope.ServiceProvider.GetService<YourDbContext>()的那一行总是出现Object reference not set to an instance of an object.的异常。我可以在这里解决其他的依赖注入服务,没有问题。 - undefined

4
根据你的代码,我认为你正在构建与Github上的CleanArchitecture Repository相关的项目。https://github.com/jasontaylordev/CleanArchitecture 这个仓库和你的方法之间的主要区别在于,显然你没有使用ASP.NET,这并不是问题,但需要更多的配置工作。
这篇文章已经提到了(https://markheath.net/post/ef-core-di-azure-functions),它引用了另一篇博客文章(https://dev.to/azure/using-entity-framework-with-azure-functions-50aa),简要解释了EntityFramework迁移不能自动在Azure Function中发现您的迁移。因此,您需要实现IDesignTimeDbContextFactory的一个实例。我在微软文档中也遇到了这个问题: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory 例如,您可以将其放置在Infrastructure\Persistence\Configurations文件夹中。(再次说明,我只是假设您正在遵循CleanArchitecture存储库结构)

我在设置网站共享项目的EFcore迁移时遇到了类似的问题,因为应用程序设置(connectionString)为空。添加了一个新的类,由迁移自动运行。class AdminPortalDbContextFactory:IDesignTimeDbContextFactory <AdminPortalDbContext> { public AdminPortalDbContext CreateDbContext(string [] args) {//设置connectionString - Tyeth

1

在 AZURE Functions 中使用 DI

注意事项

在运行时处理启动类之前和之后,会进行一系列的注册步骤。因此,请注意以下几点:

启动类仅用于设置和注册。避免在启动过程中使用在启动时注册的服务。例如,不要尝试在正在注册期间记录日志的记录器中记录消息。注册过程的这一点对于您的服务来说太早了,无法使用您的服务。在运行 Configure 方法之后,Functions 运行时继续注册其他依赖项,这可能会影响您的服务操作。

依赖注入容器仅包含显式注册的类型。可注入类型的唯一可用服务是在 Configure 方法中设置的服务。因此,Functions 特定类型(如 BindingContext 和 ExecutionContext)在设置期间或作为可注入类型不可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接