Consider the following sample code, What's the most concise way to pass C# generic type as a parameter?
public dynamic MyDbSet(string typeName)
{
var typeofDbSet = typeof(DbSet<>);
Type[] typeArgs = { Type.GetType(typeName) };
var type = typeofDbSet.MakeGenericType(typeArgs);
dynamic dbsetOfT = Activator.CreateInstance(type);
return dbsetOfT;
//Call it
//var notificationTypes = MyDbSet("NotificationType");
//var list = notificationTypes.ToList();
}
Or something like this:
public dynamic MyDbSet2(string typeName)
{
var keyValuePairs = new Dictionary<string, dynamic>
{
{nameof(NotificationType), Set<NotificationType>().AsQueryable()}
};
return keyValuePairs[typeName];
//Call it
//var notificationTypes = MyDbSet2("NotificationType");
//var list = notificationTypes.ToList();
}
Activator.CreateInstance(type)
would fail since DbSet
has no public parameterless constructor.
Unless you really need to pass the type name as string, the best way to do this is creating a generic function, getting the constructor and invoking it. It would something like this:
public DbSet<T> MyDbSet<T>() where T : class
{
return (DbSet<T>)typeof(DbSet<T>).GetConstructor(BindingFlags.NonPublic |
BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null);
}
Then, you would call it by MyDbSet<NotificationType>()
Update:
Since passing the name is required, you can do this:
public dynamic MyDbSet(string typeName)
{
return typeof(DbSet<>).MakeGenericType(Type.GetType(typeName)).
GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null).Invoke(null);
}
Then, you would call it by MyDbSet("<namespace>.NotificationType")
. Specifying the namespace is required since otherwise Type.GetType
wouldn't find the type and return null
Try this extension code:
//Use var list = _context.MyDbSet("ConsoleAppEF.Student").ToList();
public static IQueryable MySet(this SchoolContext context, string typeName)
{
var T = Type.GetType(typeName);
// Get the generic type definition
MethodInfo method = typeof(SchoolContext)
.GetMethod("MySet", BindingFlags.Public | BindingFlags.Instance);
// Build a method with the specific type argument you're interested in
method = method.MakeGenericMethod(T);
return method.Invoke(context, null) as IQueryable;
}
public static IQueryable<T> Set<T>(this SchoolContext context)
{
// Get the generic type definition
MethodInfo method = typeof(SchoolContext)
.GetMethod(nameof(SchoolContext.Set), BindingFlags.Public | BindingFlags.Instance);
// Build a method with the specific type argument you're interested in
method = method.MakeGenericMethod(typeof(T));
return method.Invoke(context, null) as IQueryable<T>;
}
public static IList ToList1(this IQueryable query)
{
var genericToList = typeof(Enumerable).GetMethod("ToList")
.MakeGenericMethod(new Type[] { query.ElementType });
return (IList)genericToList.Invoke(null, new[] { query });
}
Sample DbContext:
public class SchoolContext : DbContext
{
public SchoolContext() : base("SchoolContext")
{
}
public DbSet<Student> Students { get; set; }
public virtual new DbSet<TEntity> MySet<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
Sample Entity:
public class Student : BaseEntity
{
public string LastName { get; set; }
public string FirstMidName { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
Use it:
class Program
{
static void Main(string[] args)
{
Seed();
var _context = new SchoolContext();
var list = _context.MySet("ConsoleAppEF.Student").ToList1();
}
private static void Seed()
{
var _context = new SchoolContext();
var students = new List<Student>
{
new Student{FirstMidName="Carson",LastName="Alexander"},
};
students.ForEach(s => _context.Students.Add(s));
_context.SaveChanges();
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.