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
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.