在List上执行表达式

时间:2018-03-05 13:31:04

标签: c# linq

我正在编写自定义IQueryable,以使用Web服务。 Web服务具有有限的查询功能。 所以我想要的是,查询我的IQueryable中的Web服务,然后从webservice上执行数据的给定表达式。

我的IQueryable实现如下:

public class CloudInfoQuery : IQueryable<CloudContentModel>
{
    private string _accessToken;

    public Type ElementType => typeof(CloudContentModel);

    public Expression Expression
    {
        get;
        private set;
    }

    public IQueryProvider Provider
    {
        get;
        private set;
    }

    internal CloudInfoQuery(string accessToken)
    {
        _accessToken = accessToken;
        Provider = new CloudInfoProvider(accessToken);
        Expression = Expression.Constant(this);
    }

    internal CloudInfoQuery(string accessToken, IQueryProvider provider, Expression expression) : this(accessToken)
    {
        Provider = provider;
        Expression = expression;
    }

    public IEnumerator<CloudContentModel> GetEnumerator()
    {
        return Provider.Execute<IEnumerable<CloudContentModel>>(Expression).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Execute中的IQueryProvider如下所示:

public object Execute(Expression expression)
{
    List<CloudContentModel> cloudContent = GetContentFromWebService(expression);
    return cloudContents.AsQueryable().Provider.Execute(mExpression):
}

我正在从查询中分析可能的过滤器并将它们用于Web服务调用。 之后我想在结果上执行表达式,以便所有过滤器都适用,web服务不支持。

但是当我这样做的时候,由于Expression = Expression.Constant(this);中的CloudInfoQuery,会有无限循环。

我尝试删除Expression,这会导致表达式树出现无限循环,或者通过更新MethodCallExpression来更改它。 但后来我将面临System.ArgumentException: Static method requires null instance, non-static method requires non-null instance。 以下,我尝试过并导致上述异常:

从树中删除表达式

MethodCallExpression mExpression = expression as MethodCallExpression;
mExpression = mExpression.Update(expression, mExpression.Arguments.Skip(1));

使用新表达式更新为

MethodCallExpression mExpression = expression as MethodCallExpression;
mExpression = mExpression.Update(Expression.Constant(cloudContents), mExpression.Arguments.Skip(1));

在表达式树的顶部设置一个新表达式

MethodCallExpression mExpression = expression as MethodCallExpression;
List<Expression> exs = new List<Expression>();
exs.Add(Expression.Constant(cloudContents));
exs.AddRange(mExpression.Arguments.Skip(1));
mExpression = mExpression.Update(expression, exs);

我也试图像这样阻止无限循环:

private IEnumerabe<CloudContentModel> _cloudContents;
public object Execute(Expression expression)
{
    if(_cloudContents != null)
    {
        return _cloudContents;
    }
    List<CloudContentModel> cloudContent = GetContentFromWebService(expression);
    _cloudContents = cloudContents.AsQueryable().Provider.Execute(mExpression);
    return _cloudContents:
}

但是其他过滤器无效。 那么如何将表达式传递给另一个List?

1 个答案:

答案 0 :(得分:2)

好的,多亏了相关部分,我找到了一个受此启发的解决方案:How do you transfer the execution of a Expression created by an IQueryable object to a IEnumerable?

public class CloudInfoQuery : IQueryable<CloudContentModel>
{
    private string _accessToken;
    private List<CloudContentModel> _cloudContents = new List<CloudContentModel>();

    public Type ElementType => typeof(CloudContentModel);

    public Expression Expression
    {
        get;
        private set;
    }

    public IQueryProvider Provider
    {
        get;
        private set;
    }

    internal CloudInfoQuery(string accessToken)
    {
        _accessToken = accessToken;
        Provider = new CloudInfoProvider(accessToken, _cloudContents);
        Expression = _cloudContents.AsQueryable().Expression;
    }

    internal CloudInfoQuery(string accessToken, IQueryProvider provider, Expression expression) : this(accessToken)
    {
        Provider = provider;
        Expression = expression;
    }

    public IEnumerator<CloudContentModel> GetEnumerator()
    {
        return Provider.Execute<IEnumerable<CloudContentModel>>(Expression).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class CloudInfoProvider : IQueryProvider
{
    private string _accessToken;
    private List<CloudContentModel> _cloudContents = null;

    public CloudInfoProvider(string accessToken, List<CloudContentModel> cloudContent)
    {
        _accessToken = accessToken;
        _cloudContents = cloudContent;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return new CloudInfoQuery(_accessToken, this, expression);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return (IQueryable<TElement>)new CloudInfoQuery(_accessToken, this, expression);
    }

    public object Execute(Expression expression)
    {
        _cloudContent.Clear();
        _cloudContent.AddRange(GetContentFromWebService(expression));
        return cloudContents.AsQueryable().Provider.CreateQuery(expression):
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return (TResult)Execute(expression);

    }
}

诀窍是,使用来自同一实例的表达式,稍后将包含要过滤的数据。

Expression = _cloudContents.AsQueryable().Expression;