如何制作LINQ

我已经实现了一个基本的(天真的)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是一个MethodCallExpressionTResult是一个string ,显然这个扼流圈,所以我想我会执行该darn的事情:

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);
    }
}

我需要对我的MethodCallExpression执行什么操作才能将它传递给LINQ-to-Objects的Select方法? 我是否正确地接近这个?


(积分给Sergey Litvinov)

以下是有效的代码:

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 });

关键是直接从Queryable类型获取Select方法,使用lambda的returnType使其成为泛型方法,然后从viewSet.ToList().AsQueryable()调用它。

链接地址: http://www.djcxy.com/p/34767.html

上一篇: How to make LINQ

下一篇: What is the current status of the WebCL implementation on major browsers?