Question:

I have a question regarding type casting / generics - Here's the signature of my method:

public bool FilterMyModel<T>(T model)

T is type Person, and model is an object that is actually of type ExtendedPerson, a child class of Person.

I need to create an IQueryable that has ElementType of ExtendedPerson and contains the object that's passed in the model parameter.**

What I've Tried:

I tried each of these 3 ways of constructing the queryable, but in all 3 cases, the resulting queryable's ElementType is Person (and if I feed that IQueryable into the filtering code it doesn't work).

var query = new[] { model }.AsQueryable();
var query2 = new[] { model }.AsQueryable<T>();
var query3 = ((IEnumerable<T>)new[] { model }).AsQueryable<T>();

I also tried casting the model to the ExtendedPerson type before declaring the IQueryable like this, but got error "{"The best overloaded method match for 'MyNamespace.CastHelper.CastValue(System.Linq.IQueryable)' has some invalid arguments"}" on the line var modelCast = castHelper.CastValue(model);

//// Cast model (to handle Extended data types, e.g. ExtendedPerson, passed to this method as bast type, e.g. Person)
Type castHelperGenericBase = typeof(CastHelper<,>);
Type[] castTypeArgs = { typeof(T), model.GetType() };
Type castHelperType = castHelperGenericBase.MakeGenericType(castTypeArgs);
dynamic castHelper = Activator.CreateInstance(castHelperType);
var modelCast = castHelper.CastValue(model);

Where this is the definition of CastHelper:

public class CastHelper<T,U> where U:T
{
    public IQueryable<U> CastValue(IQueryable<T> query) 
    {
        return (IQueryable<U>)query;
    }
}

Is anyone able to assist me in figuring out how to create an IQueryable from model that will have ElementType of ExtendedPerson?

** Note: I believe that having an IQueryable<> with ElementType=ExtendedPerson will make the filtering work - what I actually know for sure is that if I'm coming from the db instead of from a model object, and I query the ExtendedPersons table into an IQueryable, that IQueryable's ElementType is ExtendedPerson, and if I then cast that to IQueryable in a dynamic way, the filtering works

Note2: I know that many will question whether I SHOULD do such a thing - the reason why I want to is that we're allowing customers to add extensions to existing types in an existing app dynamically, and trying to keep the amount of code that has to be loaded to the appdomain dynamically at runtime to a minimum - so trying to make the part of the code that really needs to know the "true" type of the extended objects use reflection to do so, and everywhere else to pass them around as the parent type, and to dynamically load code only for the extension class definitions themselves if possible.

Many thanks for your assistance!!

1

There are 1 answers

0
user756366 On BEST ANSWER

The key to answering this question is knowing how to close a generic method. There are 2 options for where this can be done to fix the issue:

1) My original question asked how to directly cast the object inside of the method that received it as a parameter of the wrong type. That is answered here: stackoverflow.com/questions/4925718/c-dynamic-runtime-cast

2) A second option is to call the method passing the parameter as an ExtendedPerson instead of as a Person - the below causes T to be read as the correct type:

var openGenericMethod = myFilteringClass.GetType().GetMethod("FilterMyModel");
MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(model.GetType());
closedGenericMethod.Invoke(viewFilter, new object[] { model });

Then, in FilterMyModel, I just have to define the queryable using a generic version of AsQueryable, not the non-generic version, as follows:

var query = new[] { model }.AsQueryable<T>();

Additional Notes: - "myFilteringClass" is the class in which the "FilterMyModel" method shown above is defined. - If it were not possible to change the value of T passed into the FilterMyModel method, I could just as easily have nested things a level deeper by defining an additional generic method in the same class, and calling it from FilterMyModel in this same way, having the same effect