0

I have a project, in business it will creates table dynamicaly, its working with netcore3.0 and EF. When an instance of dbcontext is created after dynamic table is created, I will use Assembly Emit to create a new type of the table, and use OnModelCreating method to add dbsets corresponding to tables.

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Book> Books { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        //Use assmbly emit to create dynamic types
        var types = CreateDynamicTypes();

        foreach (var type in types)
        {
            builder.Entity(type);
        }

        base.OnModelCreating(builder);
    }
}

But when a table is created after the dbcontext is created, I dont know how to add new dbset yet, because the OnModelCreating only run 1 time.

The question: How do I add new dbsets to an instance of dbcontext after its created?

1 Answer 1

1

OnModelCreating run only 1 time (when it first initialized) because of performance overhead.

There is one way, to bypass this, by using "Model Customizer"

First, you need some tweaking in OnConfiguring (you need to override basic implementation)

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);

        var serviceCollection = new ServiceCollection()
            .AddEntityFrameworkSqlServer();


        serviceCollection = serviceCollection.AddSingleton<IModelCustomizer, YourModelCustomizer>();

        var serviceProvider = serviceCollection.BuildServiceProvider();

        optionsBuilder
            .UseInternalServiceProvider(serviceProvider);
    }

And your Customizer should look like

public class YourModelCustomizer : ModelCustomizer
    {
        public override void Customize(ModelBuilder modelBuilder, DbContext dbContext)
        {
            base.Customize(modelBuilder, dbContext);

            var entityTypeBuilderCart = modelBuilder.Entity<Models.Cart>()
                .ToTable("ABC");
            entityTypeBuilderCart.Property(a => a.UserId).HasColumnName("XYZ");
            entityTypeBuilderCart.Property(a => a.ContractorId).HasColumnName("DFG");
            entityTypeBuilderCart.Ignore(a => a.CompanyId);

            var entityTypeBuilderCartArticle = modelBuilder.Entity<Models.CartArticle>()
                .ToTable("IJK");
            entityTypeBuilderCartArticle.Property(a => a.UserId).HasColumnName("QWE");
        }

        public YourModelCustomizer(ModelCustomizerDependencies dependencies) : base(dependencies)
        {
        }
    }

I hope it will help you.

Be aware that this kind of configuration may cause performance issue. This code works in EF Core 2.x, in EF 3.x may be some changes, and this code might need some changes.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your fast reply. I'll try it
How about perfomance if Customize(ModelBuilder, DbContext) call every request?
I could try to do some benchmarks when I will have some time. It may be better in you application to separate DbContext that use ModelCustomizer and another that not use it, because when ModelCustomizer is called, EF can’t fully utilize cache and this kind of approach is not recommended for applications, it’s just better to rethink your application and use procedures instead of changing your models every time. Calling OnModelCreating only once was “as designed” a goal for people that were creating this framework.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.