Reputation: 185
I have a BindingSource
whose DataSource
(defined as an object) could be any type of IEnumerable
class at runtime (such as IList<Foo>
). I need to convert it to an IQueryable<T>
so that I can pass it in to a generic extension:
IOrderedQueryable<TEntity> OrderUsingSortExpression<TEntity>(this IQueryable<TEntity> source, string sortExpression) where TEntity : class
So far I have this:
string order = "Message ASC";
Type thetype = bsTotalBindingSource.DataSource.GetType().GetGenericArguments()[0];
IEnumerable<object> totalDataSource = ((IEnumerable<object>)(bsTotalBindingSource.DataSource));
//Blowing up on this next line with 'System.Linq.Queryable is not a GenericTypeDefinition. MakeGenericType may only be called on a type for which Type.IsGenericTypeDefinition is true.'
MethodInfo asQueryableMethod = typeof(Queryable).MakeGenericType(thetype).GetMethod("AsQueryable", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(IQueryable<>) }, null);
MethodInfo genericAsQueryableMethod = asQueryableMethod.MakeGenericMethod(thetype);
MethodInfo orderUsingSortExpressionMethod = GetType().GetMethod("OrderUsingSortExpression");
MethodInfo genericUsingSortExpressionMethod = orderUsingSortExpressionMethod.MakeGenericMethod(thetype);
bsTotalBindingSource.DataSource = genericUsingSortExpressionMethod.Invoke(this, new object[] { genericAsQueryableMethod.Invoke(totalDataSource, null), order });
As you can see, the end goal here is to be able to take something from a DataSource, get its RuntimeType of IEnumerable<T>
where T
can be whatever, then call AsQueryable<T>
so it can be passed into a function which accepts an IQueryable<T>
.
EDIT After digging around to find specifically the methods I am looking for I have gotten a bit farther on the problem. It now looks like this:
string order = "Message ASC";
Type thetype = bsTotalBindingSource.DataSource.GetType().GetGenericArguments()[0];
//Returns the AsQueryable<> method I am looking for
MethodInfo asQueryableMethod = typeof(Queryable).MakeGenericType(thetype).GetMethods()[1];
MethodInfo genericAsQueryableMethod = asQueryableMethod.MakeGenericMethod(thetype);
MethodInfo orderUsingSortExpressionMethod = typeof(SortExtension)GetType().GetMethods()[0];
MethodInfo genericUsingSortExpressionMethod = orderUsingSortExpressionMethod.MakeGenericMethod(thetype);
bsTotalBindingSource.DataSource = genericUsingSortExpressionMethod.Invoke(this, new object[] { genericAsQueryableMethod
//blows up here with 'Object of type 'System.RuntimeType' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[LogRecordDTO]'.'
.Invoke(bsTotalBindingSource.DataSource, new object[] {thetype}), order });
Upvotes: 10
Views: 27789
Reputation: 5791
You say you want to convert IEnumerable<T>
to IQueryable<T>
where T's type is unknown.
private void Test<T>(IEnumerable<T> x)
{
var queryableX = x.AsQueryable();
}
NOTE: you need to be using System.Linq
.
Upvotes: 16
Reputation: 5791
I think the simplest way would be to overload (or add an extension method) to the method that you want to call, taking in an IEnumerable as an argument. Then simply call the other method from it. Here's an example of what I mean.
public class MyTestClass
{
public void Run()
{
List<string> myList = new List<string>() { "one", "two" };
object myListObject = (object)myList;
Test(myListObject);
}
private void Test(object myListObject)
{
Type myGenericType = myListObject.GetType().GetGenericArguments().First();
MethodInfo methodToCall = typeof(MyTestClass).GetMethods().Single(
method => method.Name.Equals("GenericMethod") && method.GetParameters().First().Name.Equals("myEnumerableArgument"));
MethodInfo genericMethod = methodToCall.MakeGenericMethod(myGenericType);
genericMethod.Invoke(this, new object[] { myListObject });
}
public void GenericMethod<T>(IQueryable<T> myQueryableArgument)
{
}
public void GenericMethod<T>(IEnumerable<T> myEnumerableArgument)
{
GenericMethod<T>(myEnumerableArgument.AsQueryable());
}
}
Upvotes: 1
Reputation: 50184
I think the following might work:
// Get generic type argument for everything
Type theType = bsTotalBindingSource.DataSource
.GetType().GetGenericArguments()[0];
// Convert your IEnumerable<?>-in-an-object to IQueryable<?>-in-an-object
var asQueryable =
// Get IEnumerable<?> type
typeof(IEnumerable<>)
.MakeGenericType(new[] { theType })
// Get IEnumerable<?>.AsQueryable<?> method
.GetMethod(
"AsQueryable",
new Type[] { theType },
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.Public)
// Call on the input object
.Invoke(bsTotalBindingSource.DataSource, new object[0]);
// Sort this queryable
var asSortedQueryable =
// Get the YourType<?> generic class your sorting generic method is in?
// If your class isn't generic I guess you can skip the MakeGenericType.
typeof(yourtype)
.MakeGenericType(new[] { theType })
// Get the OrderUsingSortExpression<?> method
.GetMethod(
"OrderUsingSortExpression",
new Type[] { theType },
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.Public)
// Call on the IQueryable<?> object
.Invoke(dataSource, new object[] { order });
// Set this object to the data source
bsTotalBindingSource.DataSource = asSortedQueryable;
As a disclaimer, I'm not massively clear on this. e.g. when getting AsQueryable<T>
from IEnumerable<T>
, do you need to pass in the generic argument? But I do believe what you want to do is possible, and does need reflection, and I do think this is roughly right.
Upvotes: 0