Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Create a DbContextFactory in .NETCore 3.1 & Blazor

I'm following guidelines in how to setup EF Core to work safely in Blazor & .NET Core 3.1. The MS documentation is here: https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-3.1

In the instructions, advice is to create a DbContextFactory which is used to create a dbcontext in each service. All makes sense in the Blazor world, but the code won't compile as AddDbContextFactory does not exist. If there's another way to do it in .Net Core 3.1/ EF Core 3 - I can't see it.

services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db")
    .EnableSensitiveDataLogging());
like image 872
Craig Avatar asked Dec 11 '22 00:12

Craig


1 Answers

I found this extension method that the Microsoft docs page is using in its sample github project:

        public static IServiceCollection AddDbContextFactory<TContext>(
            this IServiceCollection collection,
            Action<DbContextOptionsBuilder> optionsAction = null,
            ServiceLifetime contextAndOptionsLifetime = ServiceLifetime.Singleton)
            where TContext : DbContext
        {
            // instantiate with the correctly scoped provider
            collection.Add(new ServiceDescriptor(
                typeof(IDbContextFactory<TContext>),
                sp => new DbContextFactory<TContext>(sp),
                contextAndOptionsLifetime));

            // dynamically run the builder on each request
            collection.Add(new ServiceDescriptor(
                typeof(DbContextOptions<TContext>),
                sp => GetOptions<TContext>(optionsAction, sp),
                contextAndOptionsLifetime));

            return collection;
        }

And the factory class is here:

    public class DbContextFactory<TContext> 
        : IDbContextFactory<TContext> where TContext : DbContext
    {
        private readonly IServiceProvider provider;

        public DbContextFactory(IServiceProvider provider)
        {
            this.provider = provider;
        }

        public TContext CreateDbContext()
        {
            if (provider == null)
            {
                throw new InvalidOperationException(
                    $"You must configure an instance of IServiceProvider");
            }

            return ActivatorUtilities.CreateInstance<TContext>(provider);
        }
    }

GetOptions method:

private static DbContextOptions<TContext> 
    GetOptions<TContext>(Action<DbContextOptionsBuilder> action,
    IServiceProvider sp = null) where TContext: DbContext 
{
    var optionsBuilder = new DbContextOptionsBuilder < TContext > ();
    if (sp != null) 
    {
        optionsBuilder.UseApplicationServiceProvider(sp);
    }
    action?.Invoke(optionsBuilder);
    return optionsBuilder.Options;
}
like image 152
Umair Avatar answered Jan 11 '23 12:01

Umair