IQueryable OfType<T> where T is a runtime Type
I need to be able to get some开发者_如何学Gothing similar to the following to work:
Type type = ??? // something decided at runtime with .GetType or typeof;
object[] entityList = context.Resources.OfType<type>().ToList();
Is this possible? I am able to use .NET 4 if anything new in that allows this.
You can call it by reflection:
MethodInfo method = typeof(Queryable).GetMethod("OfType");
MethodInfo generic = method.MakeGenericMethod(new Type[]{ type });
// Use .NET 4 covariance
var result = (IEnumerable<object>) generic.Invoke
(null, new object[] { context.Resources });
object[] array = result.ToArray();
An alternative would be to write your own OfTypeAndToArray
generic method to do both bits of it, but the above should work.
Looks like you’ll need to use Reflection here...
public static IEnumerable<object> DyamicOfType<T>(
this IQueryable<T> input, Type type)
{
var ofType = typeof(Queryable).GetMethod("OfType",
BindingFlags.Static | BindingFlags.Public);
var ofTypeT = ofType.MakeGenericMethod(type);
return (IEnumerable<object>) ofTypeT.Invoke(null, new object[] { input });
}
Type type = // ...;
var entityList = context.Resources.DynamicOfType(type).ToList();
what about ...
public static IList OfTypeToList(this IEnumerable source, Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return
(IList) Activator.CreateInstance(
typeof(List<>)
.MakeGenericType(type),
typeof(System.Linq.Enumerable)
.GetMethod(nameof(System.Linq.Enumerable.OfType),
BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(type)
.Invoke(null, new object[] { source }));
}
A solution to handle multiple types is
public static IQueryable<TEntity> OfTypes<TEntity>(this DbSet<TEntity> query, IEnumerable<Type> types ) where TEntity : class
{
if( types.Count() == 0 ) return query;
var lambda = GetOfOnlyTypesPredicate( typeof(TEntity), types.ToArray() );
return query.OfType<TEntity>().Where( lambda as Expression<Func<TEntity,bool>>);
}
public static LambdaExpression GetOfOnlyTypesPredicate( Type baseType, Type[] allowed )
{
ParameterExpression param = Expression.Parameter( baseType, "typeonlyParam" );
Expression merged = Expression.TypeIs( param, allowed[0] );
for( int i = 1; i < allowed.Length; i++ )
merged = Expression.OrElse( merged, Expression.TypeIs( param, allowed[i] ));
return Expression.Lambda( merged, param );
Based on @Jon Skeet's answer, here's a LINQ extension method:
public static class QueryableExtensions
{
public static IQueryable<TSource> OfType<TSource>(this IQueryable<TSource> queryable,
Type runtimeType)
{
var method = typeof(Queryable).GetMethod(nameof(Queryable.OfType));
var generic = method.MakeGenericMethod(new[] { runtimeType });
return (IQueryable<TSource>)generic.Invoke(null, new[] { queryable });
}
}
Purely on your question to use "Generics", No it is not possible.
Generics is a compile time feature, and not a runtime discovery. For runtime, you need to use Reflection or Dynamic.
Resource[] entityList = context.Resources
.Where(t => t.GetType() == typeof(HumanResource))
.ToArray();
This worked for me with...
Type type = queryable.GetType().GenericTypeArguments[0];
精彩评论