0

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

Haseeb Sd
  • 236
  • 3
  • 9
  • Are the tables to be cached known at compile time? – Andrew Skirrow Sep 20 '19 at 14:31
  • 1
    _"I tried following, but didn't work;"_ Did not work is not a sufficient problem description. Please add what type of problem you have. What you expect and what you get. ErrorMesages? Stacktraces? ... – Fildor Sep 20 '19 at 14:31
  • 1
    In my opinion, you shouldn't _ever_ be returning a collection of `object`, unless you have a very good reason to. The whole point of a strongly typed language is that you know exactly what types you are working with. Don't make one method that returns a multitude of data types - you'll have a hard time parsing it later. – gunr2171 Sep 20 '19 at 14:32
  • Possible duplicate of [Calling generic method with Type variable](https://stackoverflow.com/questions/3957817/calling-generic-method-with-type-variable) – gunr2171 Sep 20 '19 at 14:35
  • The error is 'x' is a variable but is used like a type – Haseeb Sd Sep 20 '19 at 14:37
  • btw: Caching is hard. Why reinvent the wheel? When there are tested solutions like [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis)? (Not affiliated) – Fildor Sep 20 '19 at 14:42
  • Caching is not hard at all in .net core. Built in, [in-memory cache](https://www.infoworld.com/article/3230129/how-to-use-in-memory-caching-in-aspnet-core.html) is very easy to use. – Haseeb Sd Sep 20 '19 at 15:02

2 Answers2

1

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);
}
Alberto
  • 15,626
  • 9
  • 43
  • 56
  • The GetTableData() bit is fine in your answer. Can you please show how to use foreach loop to call this method, as I have 50 other entities (with static small data set, so do not worry about cache getting explode) in my project and I do not want to write 50 times the cache.Set() line. Thanks – Haseeb Sd Sep 20 '19 at 14:59
  • Many thanks, I have implemented very similar solution to yours. Special thanks to @gunr2171 for pointing to the right direction. – Haseeb Sd Sep 20 '19 at 16:25
0

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>();
}
Haseeb Sd
  • 236
  • 3
  • 9