【问题标题】:Execute expression on List在 List 上执行表达式
【发布时间】:2018-03-05 13:31:04
【问题描述】:

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

我的 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();
    }
}

我在IQueryProvider 中的Execute 看起来像这样:

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

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

但是当我这样做时,会出现无限循环,因为我的CloudInfoQuery 中有Expression = Expression.Constant(this);

我试图删除表达式,这会导致表达式树出现无限循环,或者通过更新 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 呢?

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    好的,感谢相关部分,我找到了一个受此启发的解决方案: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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多