IQueryable OfType<T> where T is a runtime Type
Asked Answered
S

8

27

I need to be able to get something 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.

Schluter answered 8/9, 2010 at 16:13 Comment(2)
Must it be IQueryable? From your example, IEnumerable seems sufficient.Phionna
@leppie I don't understand your point..Schluter
V
43

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.

Viddah answered 8/9, 2010 at 16:23 Comment(9)
Skeet this works, what a legend - I'm just going to use SQL profiler to see if it's doing the filtering after retrieving the collection or as part of the query.. unless you know the answer to that also!Schluter
@Shahin: It should be doing the right thing - it's calling Queryable.Where instead of Enumerable.Where, after all.Viddah
@Skeet do you have any idea why it may be caching from the first result set it received?Schluter
@Shahin: Are you reusing the same context? If so, that's probably what's wrong.Viddah
Thanks for this solution, better than my approach which was with beginner understanding on covariance, this is much cleaner.Schluter
I used this but I had to cast Resources.AsQueryable() first for it to workCompression
As I spent like 20 minutes trying to figure out something, I will comment it here so that others don't. It passes the context.Resources object inside an array in the 'parameters' parameter because OfType is an extension method and takes as a parameter the class that it is extending (it blew my mind when I got that).Usurer
I see this returns an Array with .ToArray(), so I guess this executes the query once it's called. Would be too different to keep the IQueryable<T>() as result so you can keep chaining other methods before finally running the query?Saraband
@CesarD: Well sort of - you can cast to IQueryable<object> instead, but then you wouldn't have a usefully-typed query to work with.Viddah
A
8

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();
Adumbrate answered 8/9, 2010 at 16:23 Comment(0)
H
2

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 }));
    }
Hill answered 27/2, 2016 at 11:18 Comment(0)
P
2

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 );
Prud answered 11/4, 2018 at 16:6 Comment(0)
L
2

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 });
    }
}
Lashundalasker answered 8/8, 2019 at 5:48 Comment(0)
P
0
Resource[] entityList = context.Resources
                              .Where(t => t.GetType() == typeof(HumanResource))
                              .ToArray();
Phionna answered 8/9, 2010 at 16:16 Comment(2)
Will that not return the entire contents of Resources before running the where against it? Using OfType on this context runs it in the SQL (I'm using Zentity)Schluter
In addition to what Shahin said, this is also wrong for polymorphic types.Adumbrate
T
0

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.

Tilsit answered 8/9, 2010 at 16:24 Comment(3)
Meh, I don't like the characterization of "generics is a compile time feature." It's that and also demonstrably a runtime feature (unlike Java).Harkness
You may not like it but what matters is that generics is a compile time feature. Generic type is compiled with the knowledge of the T. If you can show otherwise, please enlighten me.Tilsit
If it weren't a runtime feature how could MakeGenericType work?Marlette
L
0

This worked for me with...

Type type = queryable.GetType().GenericTypeArguments[0];
Lorenlorena answered 15/4, 2021 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.