I am using .net core 2.1 with entityframework core.
I have different models/entities/types defined in my project. e.g. Student, Class, Teacher.
I am getting the table data for these models to set in my cache.
At the moment, I am doing this;
string[] tablesToBeCached = { "Student", "Class", "Teacher" };
foreach(var table in tablesToBeCached)
{
cache.Set(key, GetTableData(dbContext, table));
}
and the function GetTableData() is defined as follows;
public IEnumerable<object> GetTableData(DBContext dbContext, string tableName)
{
switch (tableName)
{
case "Student":
return dbContext.Student;
case "Class":
return dbContext.Class;
case "Teacher":
return dbContext.Teacher;
default:
return null;
}
}
I want this code to be smart and short.
I tried following, but didn't work; (The error is 'x' is a variable but is used like a type)
List<object> entities = new List<object> { typeof(Student), typeof(Class), typeof(Teacher) };
entities.ForEach(x => GetTableData(x, dbContext));
public IEnumerable<object> GetTableData(object x, DBContext dbContext)
{
return dbContext.Set<x>();
}
Can someone please help? Is it even possible in C#?
As someone pointed out in the comments, you should go with generics:
cache.Set(key1, GetTableData<Student>(dbContext));
cache.Set(key2, GetTableData<Class>(dbContext));
cache.Set(key3, GetTableData<Teacher>(dbContext));
public static IEnumerable<T> GetTableData<T> (DBContext dbContext)
{
return dbContext.Set<T>();
}
To avoid writing the same code (cache.Set
) foreach entity, you can use reflection, but your entities should implement some kind of common interface or base class.
For example, suppose your entities implement a common interface IEntity
:
interface IEntity {}
class Student: IEntity {}
class Teacher: IEntity {}
then you can
1 retrieve all the types that implement IEntity:
var type = typeof(IEntity);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
2 call the GetTableData method in this way:
MethodInfo method = GetType.GetMethod("GetTableData ");
foreach (var entityType in types)
{
MethodInfo genericMethod = method.MakeGenericMethod(entityType);
genericMethod.Invoke(this, null);
}
I have implemented very similar solution as @Alberto answered (but I have avoided IEntity interface bit). Special thanks to @gunr2171 for pointing to the right direction. My solution is as follows;
MethodInfo methodInfo = typeof(CacheSettings).GetMethod("GetTableData");
string[] tablesToBeCached = { "Student", "Class", "Teacher" };
object[] parameters = new object[] { myDBContextObj };
foreach(var tblToBeCached in tablesToBeCached)
{
string key = $"{tblToBeCached}";
MethodInfo getTableDataMethod = methodInfo.MakeGenericMethod(Type.GetType($"Namespace.{tblToBeCached}, AssemblyName"));
cache.Set(key, getTableDataMethod.Invoke(null, parameters));
}
and the GetTableData() method is just one liner (Happy days 😊)
public static IEnumerable<T> GetTableData<T>(MyDBContext dbContext) where T : class
{
return dbContext.Set<T>();
}