3

データベースからデータをフェッチする方法がありますが、これは一般的です。

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

データはフィルタリングされていないdbエンティティのコレクションであり、GetDataはフィルタリング、並べ替え、スキップ、そのコレクションの取得を行います。

IQueryableタイプの変数(たとえば、Document)を最初のパラメーターとして指定すると、通常どおり、もちろん、次のように機能します。

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 INDEEDはIQueryable(IQueryable)であり、GetDataの引数として機能できるはずです。しかし、私はそれへの「オブジェクト」参照しか持っておらず、当然、そのように使用することはできません。

だから、私の質問は、oがまさにそれであるときに「o」を「IQueryable」に動的にキャストする方法はありますか?キャストはコンパイル時のものであることは知っていますが、誰かが何らかの回避策を持っていることを望んでいます。または多分私はやりすぎです。

4

2 に答える 2

4

この機能を利用できdynamicます。ダイナミックは、すでにリフレクションを実行する必要がある状況に最適です。

dynamic o = Expressin.Lambda(...
于 2013-02-23T20:33:00.807 に答える
1

この仲間の負荷を取得します。

初め。GetData <T>メソッドを囲むクラスはFooと呼ばれると想定しています:

public static class Foo {

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

次に、 GetData <>のMethodInfoを反映しようとします(つまり、実際のテンプレート、一般的な定義を意味し、閉じた特殊化ではありません)。私たちは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
        }
    }


}
于 2013-02-23T21:01:24.800 に答える