7

私の目的には問題なく動作する基本的な (素朴な?) LINQ プロバイダーを実装しましたが、対処したい癖がいくつかありますが、その方法がわかりません。例えば:

// performing projection with Linq-to-Objects, since Linq-to-Sage won't handle this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);

私のIQueryProvider実装には、次のCreateQuery<TResult>ような実装がありました。

public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
    return (IQueryable<TResult>)Activator
        .CreateInstance(typeof(ViewSet<>)
        .MakeGenericType(elementType), _view, this, expression, _context);
}

明らかに、これはExpressionが aMethodCallExpressionでありTResultである場合にチョークします。string

public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
    var elementType = TypeSystem.GetElementType(expression.Type);
    if (elementType == typeof(EntityBase))
    {
        Debug.Assert(elementType == typeof(TResult));
        return (IQueryable<TResult>)Activator.CreateInstance(typeof(ViewSet<>).MakeGenericType(elementType), _view, this, expression, _context);
    }

    var methodCallExpression = expression as MethodCallExpression;
    if(methodCallExpression != null && methodCallExpression.Method.Name == "Select")
    {
        return (IQueryable<TResult>)Execute(methodCallExpression);
    }

    throw new NotSupportedException(string.Format("Expression '{0}' is not supported by this provider.", expression));
}

そのため、実行するvar vendorCodes = context.Vendors.Select(e => e.Key);private static object Execute<T>(Expression,ViewSet<T>)オーバーロードが発生し、最も内側のフィルター式のメソッド名がオンになり、基になる API で実際の呼び出しが行われます。

さて、この場合、Selectメソッド呼び出し式を渡しているので、フィルター式はnullあり、switchブロックはスキップされます-これは問題ありません-私が行き詰まっている場所は次のとおりです。

var method = expression as MethodCallExpression;
if (method != null && method.Method.Name == "Select")
{
    // handle projections
    var returnType = method.Type.GenericTypeArguments[0];
    var expType = typeof (Func<,>).MakeGenericType(typeof (T), returnType);

    var body = method.Arguments[1] as Expression<Func<T,object>>;
    if (body != null)
    {
        // body is null here because it should be as Expression<Func<T,expType>>
        var compiled = body.Compile();
        return viewSet.Select(string.Empty).AsEnumerable().Select(compiled);
    }
}

MethodCallExpressionLINQ-to-Objects のSelectメソッドに渡すことができるようにするには、何をする必要がありますか? 私はこれに正しく近づいていますか?

4

1 に答える 1

3

セルゲイ・リトヴィノフのクレジット

機能したコードは次のとおりです。

var method = expression as MethodCallExpression;
if (method != null && method.Method.Name == "Select")
{
    // handle projections
    var lambda = ((UnaryExpression)method.Arguments[1]).Operand as LambdaExpression;
    if (lambda != null)
    {
        var returnType = lambda.ReturnType;
        var selectMethod = typeof(Queryable).GetMethods().First(m => m.Name == "Select");
        var typedGeneric = selectMethod.MakeGenericMethod(typeof(T), returnType);
        var result = typedGeneric.Invoke(null, new object[] { viewSet.ToList().AsQueryable(), lambda }) as IEnumerable;
        return result;
    }
}

今これ:

var vendorCodes = context.Vendors.ToList().Select(e => e.Key);

次のようになります。

var vendorCodes = context.Vendors.Select(e => e.Key);

そして、これを行うこともできます:

var vendors = context.Vendors.Select(e => new { e.Key, e.Name });

Select重要なのは、メソッドを型から直接フェッチQueryableし、ラムダの を使用してジェネリック メソッドにしてreturnTypeから、 off を呼び出すことでしたviewSet.ToList().AsQueryable()

于 2016-06-28T17:18:06.443 に答える