Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core Migrations in Azure Function startup

According to https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection the service provider should not be used until AFTER the startup has completed running. Indeed, if I try to get a registered service it will fail.

Example:

[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;
        }
    }

This produces the following error

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.

Is there a good option for migrating and seeding during startup?

like image 977
greven Avatar asked May 14 '26 13:05

greven


2 Answers

The easiest way I found to run code after startup was by registering a custom IWebJobsStartup by using the WebJobsStartupAttribute (the FunctionsStartupAttribute actually also inherits from this attribute). In the WebJobsStartup class you'll need to register your extension using the AddExtension where you are able to use dependency injection and seed your database. My code:

[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.
        }
    }
}
like image 78
Martijn Lentink Avatar answered May 16 '26 01:05

Martijn Lentink


According to your code, I assume that you're building something aligned to the CleanArchitecture Repository on Github. https://github.com/jasontaylordev/CleanArchitecture

The main difference between this repo and your apporach, is that you're obviously not using ASP.NET, which is not a problem at all, but requires a little bit more configuration work.

The article already mentioned (https://markheath.net/post/ef-core-di-azure-functions) refers another blogpost (https://dev.to/azure/using-entity-framework-with-azure-functions-50aa), which briefly explains that EntityFramework Migrations are not capable of auto-discovering your migrations in an Azure Function. Therefore, you need to implement an instance of IDesignTimeDbContextFactory. I also stumbled upon it in the microsoft docs: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory

You could for example place it inside your Infrastructure\Persistence\Configurations folder. (Once again, I'm only assuming that you're following the CleanArchitecture repo structure)

like image 32
EimerReis Avatar answered May 16 '26 03:05

EimerReis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!