Passing types to function in c#

c# entity-framework-core generics typeof


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;

              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#?

9/20/2019 2:54:53 PM

Accepted Answer

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);
9/20/2019 3:41:32 PM

Popular Answer

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>();

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow