7
string tableName = "TblStudents";
Dictionary<string, Type> myDictionary = new Dictionary<string, Type>()
{
    { "TblStudents", typeof(TblStudent) },
    { "TblTeachers", typeof(TblTeacher) }
};

// Context always same
DBContext dbContext = new DBContext();
DbSet dbSet = dbContext.Set(myDictionary[tableName]);

Above code is from this post where I can DbSet dynamically. How can I make this work in Entity Framework Core?

I get an error at

DbSet dbSet = dbContext.Set(myDictionary[tableName]);

seems like Set method has been changed in the new version.

Help is appreciated.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
bbusdriver
  • 1,577
  • 3
  • 26
  • 58

2 Answers2

7

If you're trying to get the DbSet<TEntity> by TEntity, use:

var dbSet = dbContext.Set<TEntity>();

If you want to call that method based off a string name and your dictionary, you'll have to use reflection.

EF Core does not appear to have a non-generic DbSet so you'll have to use one of the non-generic interfaces such as IQueryable and I'd include a Func that you can invoke to get the IQueryable instead of just the type if you insist on going the dictionary mapping route. For example:

var myDictionary = new Dictionary<string, Func<DbContext, IQueryable>>()
{
    { "TblStudents", ( DbContext context ) => context.Set<TblStudent>() }
};

var dbSet = myDictionary[ "TblStudents" ].Invoke( dbContext );
Moho
  • 15,457
  • 1
  • 30
  • 31
  • 1
    Thanks for your answer. Now if I for-loop `dbSet`, I can't get the property inside `dbSet` since it's a type of `IQueryable`. How do I solve this? – bbusdriver Oct 04 '18 at 04:43
  • 1
    get what property? If `IQueryable` doesn't provide what you need, use the interface that does. I'm not sure what you're trying to accomplish. – Moho Oct 04 '18 at 05:22
  • 1
    What if I want to use `.Add` or `.Remove` ... Looks like both methods do not belong to any interface ... I hope that I'm wrong! – bunjeeb Jun 23 '21 at 09:41
0

DbContext.Set and DbContext.Set(string name) are the only available functions for EF Core. DbContext.Set(Type) is for Framework from what I have read. Knowing this limitation, we will be able to trigger this.

public List<Type> FetchDbSetTypes()
{
    var properties = dbContext.GetType().GetProperties();
    var dbSets = new List<Type>();
    foreach (var property in properties)
    {
        var propertyType = property.PropertyType;
        if(propertyType.IsGenericType && propertyType.Name.ToLower().Contains("dbset"))
        {
            Type dbSetType = propertyType.GenericTypeArguments[0]; //point of interest here
            dbSets.Add(dbSetType);
        }
    }
    return dbSets;
}

We now have a list of the DBSets' defined strong types. Next, we need to spoof the Set based on the added types. The only way to do this is to pass an object of that type as a parameter.

This requires reflection, InternalDbSet<>, DBContext, and dynamic.

List<Type> dbsetTypes = FetchDbSetTypes();
dbsetTypes.ForEach(async dbsetType =>
{
    try
    {
        Type myType = typeof(InternalDbSet<>).MakeGenericType(dbsetType);
        //instance is only used to spoof the binding
        dynamic instance = Activator.CreateInstance(myType, context, dbsetType.Name);
        var test = await FetchFromTable(instance, 0, 10);
    }
    catch(Exception)
    {
        //might fail due to other models
    }
});

Then we call the generic method in question which takes DbSet

public async Task<List<T>> FetchFromTable<T>(DbSet<T> _, int skip = 0, int fetchSize = 10000) where T : class
{
    //DbSet<T> parameter is not needed - it will throw an Exception
    return await dbContext.Set<T>().Skip(skip).Take(fetchSize).ToListAsync();
}

Note: Make sure your DbContext properties have getters

skenneth
  • 1
  • 1