我有一种从数据库中获取数据的方法,它是通用的:
public static IQueryable<T> GetData<T>(IQueryable<T> data, some more parameters)
数据是未经过滤的数据库实体集合,GetData进行过滤,排序,跳过,接受该集合......
当我提供IQueryable类型的变量(例如,T,例如,Document)作为第一个参数时,正如我通常所做的那样,它当然有效:
IQueryable<Document> data = ...
GetData<Document>(data, ....);
现在,我需要动态地“计算”第一个参数。为此,我使用LINQ表达式,它将评估为IQueryable,但我不知道在编译时哪个T.我在想这样的事情:
Expression db = Expression.Constant(new DataModelContainer());
Expression table = Expression.Property(db, tbl); /* tbl = "Documents", this is the whole point */
Type type = table.Type.GetGenericArguments()[0];
Expression call = Expression.Call(typeof(Queryable), "AsQueryable", new Type[] { type }, table);
object o = Expression.Lambda(call, null).Compile().DynamicInvoke();
此时o INDEED IS IQueryable(IQueryable),因此应该能够作为GetData的参数。但是,我只有'对象'引用它,当然,不能像那样使用它。
所以,我的问题是:当o恰好是那时,有没有办法动态地将'o'转换为'IQueryable'。我知道演员阵容是编译时间的事情,但我希望有人有某种解决方法。或者也许我在尝试太多。
答案 0 :(得分:4)
您可以使用dynamic
功能。动态非常适用于您必须进行反射的情况:
dynamic o = Expressin.Lambda(...
答案 1 :(得分:1)
获得这个fellah的负担:
首先。我们假设围绕 GetData&lt;的类。 T&gt; 方法称为 Foo :
public static class Foo {
public static IQueryable<T> GetData<T>(IQueryable<T> data, int bar, bool bravo) {
// ... whatever
}
然后我们尝试反思 GetData&lt;的MethodInfo。 &gt; (我的意思是实际模板,通用定义,而不是它的封闭式细节)。我们试图在 Foo 类的诞生时获得(并成功)。
private static readonly MethodInfo genericDefinitionOf_getData;
static Foo() {
Type prototypeQueryable = typeof(IQueryable<int>);
// this could be any IQuerable< something >
// just had to choose one
MethodInfo getData_ForInts = typeof(Foo).GetMethod(
name: "GetData",
bindingAttr: BindingFlags.Static | BindingFlags.Public,
binder: Type.DefaultBinder,
types: new [] { prototypeQueryable, typeof(int), typeof(bool) },
modifiers: null
);
// now we have the GetData<int>(IQueryable<int> data, int bar, bool bravo)
// reffered by the reflection object getData_ForInts
MethodInfo definition = getData_ForInts.GetGenericMethodDefinition();
// now we have the generic (non-invokable) GetData<>(IQueryable<> data, int bar, bool bravo)
// reffered by the reflection object definition
Foo.genericDefinitionOf_getData = definition;
// and we store it for future use
}
然后我们编写一个方法的非泛型变体,它应该调用关于作为参数发送的实际元素类型的特定泛型方法:
public static IQueryable GetDataEx(IQueryable data, int bar, bool bravo) {
if (null == data)
throw new ArgumentNullException("data");
// we can't honor null data parameters
Type typeof_data = data.GetType(); // get the type (a class) of the data object
Type[] interfaces = typeof.GetInterfaces(); // list it's interfaces
var ifaceQuery = interfaces.Where(iface =>
iface.IsGenericType &&
(iface.GetGenericTypeDefinition() == typeof(IQueryable<>))
); // filter the list down to just those IQueryable<T1>, IQueryable<T2>, etc interfaces
Type foundIface = ifaceQuery.SingleOrDefault();
// hope there is at least one, and only one
if (null == foundIface) // if we find more it's obviously not the time and place to make assumptions
throw new ArgumentException("The argument is ambiguous. It either implements 0 or more (distinct) IQueryable<T> particularizations.");
Type elementType = foundIface.GetGenericArguments()[0];
// we take the typeof(T) out of the typeof(IQueryable<T>)
MethodInfo getData_particularizedFor_ElementType = Foo.genericDefinitionOf_getData.MakeGenericMethod(elementType);
// and ask the TypeSystem to make us (or find us) the specific particularization
// of the **GetData < T >** method
try {
object result = getData_particularizedFor_ElementType.Invoke(
obj: null,
parameters: new object[] { data, bar, bravo }
);
// then we invoke it (via reflection)
// and obliviously "as-cast" the result to IQueryable
// (it's surely going to be ok, even if it's null)
return result as IQueryable;
} catch (TargetInvocationException ex) {
// in case of any mis-haps we make pretend we weren't here
// doing any of this
throw ex.InnerException;
// rethink-edit: Actually by rethrowing this in this manner
// you are overwriting the ex.InnerException's original StackTrace
// so, you would have to choose what you want: in most cases it's best not to rethrow
// especially when you want to change that which is being thrown
}
}
}