c# - 在 List 上执行表达式

标签 c# linq

我正在编写自定义 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();
    }
}

我的 ExecuteIQueryProvider看起来像这样:

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

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

但是当我这样做的时候,会出现死循环,因为Expression = Expression.Constant(this);在我的 CloudInfoQuery .

我试图从表达式树中删除导致无限循环的表达式,或者通过更新 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:
}

但是附加的过滤器将不起作用。 那么如何将表达式传递给另一个列表呢?

最佳答案

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

关于c# - 在 List 上执行表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49111614/

相关文章:

c# - 我可以一次合并这两个列表吗

c# - PredicateBuilder 是如何工作的

c# - 将 DataGrid 可见性与其自己的 (ItemSource.Count > 0) Silverlight MVVM 绑定(bind)

c# - 如何通过 RDP 连接可靠地 "PrintScreen"(保存屏幕内容)?

c# - 尝试更好地理解共享项目并在不同的解决方案中使用它们

c# - 使用 nHibernate QueryOver 加入一个子集

c# - 如何使用 Linq 执行 IN 语法

c# - MediaElement 的 SetSource 使用继承自 IsolatedStorageFileStream 的自定义流

c# - LINQ:查询集合是否包含另一个集合中的任何元素

c# - 我如何从 bool 值切换或运算符开/关