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.

asleep
  • 4,054
  • 10
  • 34
  • 51

8 Answers8

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 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! – asleep Sep 08 '10 at 16:28
  • 1
    @Shahin: It should be doing the right thing - it's calling `Queryable.Where` instead of `Enumerable.Where`, after all. – Jon Skeet Sep 08 '10 at 16:29
  • @Skeet do you have any idea why it may be caching from the first result set it received? – asleep Sep 08 '10 at 16:58
  • @Shahin: Are you reusing the same context? If so, that's probably what's wrong. – Jon Skeet Sep 08 '10 at 17:19
  • Thanks for this solution, better than my approach which was with beginner understanding on covariance, this is much cleaner. – asleep Sep 09 '10 at 09:33
  • I used this but I had to cast `Resources.AsQueryable()` first for it to work – bevacqua Apr 23 '11 at 14:03
  • 1
    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). – Nahuel Prieto Sep 26 '14 at 17:49
  • 1
    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() as result so you can keep chaining other methods before finally running the query? – CesarD Jun 23 '17 at 20:14
  • @CesarD: Well sort of - you can cast to `IQueryable` instead, but then you wouldn't have a usefully-typed query to work with. – Jon Skeet Jun 23 '17 at 20:41
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();
Timwi
  • 65,159
  • 33
  • 165
  • 230
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 }));
    }
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 );
Drew Ogle
  • 479
  • 4
  • 4
1

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 });
    }
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
0
Resource[] entityList = context.Resources
                              .Where(t => t.GetType() == typeof(HumanResource))
                              .ToArray();
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 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) – asleep Sep 08 '10 at 16:18
  • In addition to what Shahin said, this is also wrong for polymorphic types. – Timwi Sep 08 '10 at 16:19
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.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • 2
    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). – Kirk Woll Sep 08 '10 at 16:35
  • 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. – Aliostad Sep 08 '10 at 17:39
  • If it weren't a runtime feature how could MakeGenericType work? – Casey Sep 19 '14 at 17:53
0

This worked for me with...

Type type = queryable.GetType().GenericTypeArguments[0];