2013-02-23 66 views
3

我从数据库中获取数据的方法,它是通用的:动态投对象的类型,如果类型是未知在编译时

public static IQueryable<T> GetData<T>(IQueryable<T> data, some more parameters) 

数据DB实体未经过滤的收集和的GetData呢过滤,排序,跳过,采取该集合...

当我提供类型IQueryable(T是,例如,文档)的变量作为第一个参数,我通常这样做,当然,它的作品:

IQueryable<Document> data = ... 
GetData<Document>(data, ....); 

现在,我需要动态“计算”第一个参数。为此,我使用将评估为IQueryable的LINQ表达式,但我不知道在编译时哪个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确实的IQueryable(IQueryable的),这样应该可以作为一个参数的GetData。但是,我只有'对象'的参考,并且自然不能像这样使用它。

所以,我的问题是:有什么办法可以动态地将'o'转换为'IQueryable',当o就是这样。我知道演员是编译时的事情,但我希望有人有某种解决方法。或者也许我尝试了太多。

+0

您是否尝试过做Ø' dyanamic',如'dynamic o = Expressin.Lambda ...'? – Steven 2013-02-23 20:16:24

+0

我没有,但我刚刚做了。我认为这样做会很好。开启了这么多的可能性。它发生在我身上,但总是想着它做了太多的工作。谢啦! – 2013-02-23 20:27:15

+0

等一下,我会张贴作为答案;-) – Steven 2013-02-23 20:32:00

回答

4

您可以使用dynamic功能。动态是伟大的情况下,您已经做反思:

dynamic o = Expressin.Lambda(... 
1

获得此的Fellah的负载:

第一。我们假设周围的的GetData < T>方法的类被称为

public static class Foo { 

    public static IQueryable<T> GetData<T>(IQueryable<T> data, int bar, bool bravo) { 
     // ... whatever 
    } 

然后我们尝试后的MethodInfo的的的GetData <>反映(和我的意思是实际的模板,通用的定义,而不是一个封闭的特殊化)。我们试图在诞生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 
     } 
    } 


} 
+0

一些好东西! MakeGenericMethod也考虑到了我的想法。感谢您的细致和耐心。但是,即使向方法发送IQueryable,我也可能会遇到麻烦,因此我只能将对象作为结果放置到位IQueryable 。 – 2013-02-23 21:31:42

+0

如果认为你当然可以将IQueryable query = obj做为IQueryable,然后使用方法GetData(非非泛型)。你无法做的就是说IQueryable q = obj as IQueryable (其中首字母缩写词代表SomeRuntimeKnownButSyntacticallyUnknownType :)) – 2013-02-23 21:34:59

+0

哦。哦,我的错误,现在我明白了!无论如何..自.NET 4.0以来,您知道我们有能力将IEnumerable 投射到IEnumerable (协方差)和IComparator to IComparator (contravariance)。因此,总之,至少你可以(明确地)将结果对象投射到IQueryable (因为对象与任何东西都兼容,而IQueryable与它本身是协变的)。然后由你决定,我想要做你想做的事,一般地与结果查询进行交谈(查询更多内容,或者停止并提供结果,也可能使用动态方式,或者也可能使用调度程序调用N个方法) – 2013-02-23 21:37:08

相关问题