0

I'm trying to create some tables manually and map to DbSet dynamically but it does not work. Here is my DbContext class:

public abstract class DynamicDbContext : DbContext
{
    private List<IQueryable> m_dbSets;

    public DynamicDbContext([NotNull] DbContextOptions options) :
        base(options)
    {
    }

    public DbSet<T> GetTbl<T>() where T : class
    {
        if (m_dbSets == null)
        {
            m_dbSets = new List<IQueryable>();

            foreach (var iter in GetDynTypes())
            {
                var setMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set), new[] { typeof(string) });
                IQueryable foundSet = (IQueryable)setMethod
                    .MakeGenericMethod(iter)
                    .Invoke(this, new object[] { iter.Name });

                    m_dbSets.Add(foundSet);
                }
            }
        }

        return m_dbSets.FirstOrDefault(obj => obj.ElementType == typeof(T)) as DbSet<T>;
    }
    
    protected abstract List<Type> GetDynTypes();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        foreach (var iter in GetDynTypes())
        {
            EntityTypeBuilder builder = modelBuilder.Entity(iter);
            builder.ToTable(iter.Name);
        }
    }
    #endregion
}

The class obtains the dynamic types from application's derived class and should register the classes as tables to database. Then I would like to use Linq to work with the tables, so I introduced GetTbl method which would return the DbSet<T> so the application can call Linq on this set. This method use reflection to obtain the DbSet, but I also tried directly call Set<T>(typeof(T).Name).

When I try to access the returned DbSet the result is exception: Cannot create a DbSet for 'Abc' because this type is not included in the model for the context

It looks like the table(s) are created e.g. when I run migration command I can see the tables in the database:

dotnet ef migrations add InitialCreate

The problem is probably with the table mapping to DbSet - in my GetTbl method. Or maybe I need to do something extra in OnModelCreating method.

Not sure what is the problem. Any help is appreciated :-) Thanks

Csaba
  • 301
  • 3
  • 11
  • please see this https://stackoverflow.com/questions/38532764/create-tables-on-runtime-in-ef-core – behroozbc Jun 01 '21 at 08:48
  • Does this answer your question? [Create Tables on runtime in EF Core](https://stackoverflow.com/questions/38532764/create-tables-on-runtime-in-ef-core) – behroozbc Jun 01 '21 at 08:52
  • Unfortunately this does not help. Inspecting my DB, the table(s) are created, however I can't map the table to DbSet dynamically in my GetTbl method. – Csaba Jun 01 '21 at 09:30
  • Once you register the types in OnModelCreating, you can get the DbSet's with simply `myDbContext.Set();` – David Browne - Microsoft Jun 01 '21 at 13:40

1 Answers1

1

Once you register the types in OnModelCreating, you can get the DbSet's with simply

 myDbContext.Set<T>();

And so a simpler design is to just use a single generic type that you specialize at runtime with the dynamic entity type. eg

public class DynamicDbContext<T> : DbContext where T:class
{
    public DynamicDbContext([NotNull] DbContextOptions options) :
        base(options)
    {
    }

    public DbSet<T> Entities => this.Set<T>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<T>().ToTable(typeof(T).Name);
        base.OnModelCreating(modelBuilder);
    }
}

Of course this won't create the table if it doesn't exist.

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • This works. What is interesting that my code is similar and it does not work - magic :-) – Csaba Jun 02 '21 at 11:06