在 Azure 表服务 linq 查询中运行包含运算符

Posted

技术标签:

【中文标题】在 Azure 表服务 linq 查询中运行包含运算符【英文标题】:Running a Contains operator in Azure Table Service linq query 【发布时间】:2019-04-09 16:15:37 【问题描述】:

鉴于“包含”,我想知道此查询为何/如何在 Azure 存储表上运行 are not allowed in Azure Table Service? 这不是在做我认为它在做的事情吗?它正在运行并获取值。 另外,这是先获取整个表然后过滤吗?在调试器中,它看起来直到我运行 ToList() 才完全运行?

这是我的代码,我使用包含的底线。

List<string> partitionIds = new List<string> "1", "2", "3";

var table = // get table here...

var result = table.ExecuteQuery(new TableQuery<ConnectionEntity>()); 
var queryResult = result.Where(w => partitionIds.Contains(w.PartitionKey)).ToList();

【问题讨论】:

【参考方案1】:

我知道这是一个旧帖子,但我们实际上有一个类似的问题,我没有找到更新的东西。

加载所有数据并过滤它们不是我们的选择。同样,逐个加载记录也是不可接受的解决方案。

因此,在查询中执行此操作的最简单方法是创建多个或条件。将其更改为 new TableQuery&lt;ConnectionEntity&gt;().Where(w =&gt; w.PartitionKey == "1" || w.PartitionKey == "2" || ...) 之类的内容。

这项工作很好,但当然也有一些局限性。通过我们的测试,我们得到了 400 个 BadRequest,其中包含超过 110 个条件。

但如果你知道,数量不多,你可以这样做。

我编写了一个扩展方法来动态地在 IQueryable 上执行此操作,例如 .Contains()(使用 Microsoft.Azure.Cosmos.Table 库测试)这并不容易 :)

这里是代码

    /// <summary>
    /// Convert Contains to a concatenated Or condition for Azure Table query support
    /// </summary>
    /// <typeparam name="T">Entity type</typeparam>
    /// <typeparam name="TParam">property type to check</typeparam>
    /// <param name="query">current query to extend</param>
    /// <param name="values">Values to proof</param>
    /// <param name="property">Which property should be proofed</param>
    /// <returns></returns>
    public static IQueryable<T> WhereContains<T, TParam>(this IQueryable<T> query, IEnumerable<TParam> values, Expression<Func<T, TParam>> property)
    
        var enumerable = values.ToList();
        if (!enumerable.Any())
            return query;

        Expression<Func<T, bool>> predicate = null;
        var parameter = Expression.Parameter(typeof(T), "entity");
        var propertyName = ((property.Body as MemberExpression)?.Member as PropertyInfo)?.Name 
                           ?? throw new Exception("Property can't be evaluated");
        foreach (var value in enumerable)
        
            var scope = new ExpressionScopedVariables  Value = value ;
            var filterStringExp = Expression.Constant(scope);
            var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0];
            var access = Expression.MakeMemberAccess(filterStringExp, getVariable);

            Expression<Func<T, bool>> currentExpression = Expression.Lambda<Func<T, bool>>(
                Expression.Equal(
                    Expression.Property(parameter, propertyName),
                    access), parameter);
            predicate = predicate == null ? currentExpression : Expression.Lambda<Func<T, bool>>(Expression.OrElse(predicate.Body, currentExpression.Body), predicate.Parameters);
        

        return query.Where(predicate ?? throw new InvalidOperationException());
    

    class ExpressionScopedVariables
    
        // ReSharper disable once UnusedAutoPropertyAccessor.Local
        public object Value  get; set; 
    

以及如何使用它的例子

var query = from v in _baseRepository.AsQueryable()
            where v.PartitionKey == partitionKey
            select v;

query = query.WhereContains(entityIds, v => v.RowKey);
var entities = (await query.QueryAsync()).ToList();

_baseRepository 是我们自己的 CloudTable 存储库实现,AsQueryable()QueryAsync() 是创建和执行查询的扩展方法

【讨论】:

【参考方案2】:

如您提供的网站所述,Azure 表服务不支持验证包含语句。由于数据保存在非 SQL 环境中,因此 contains 语句可能需要大量电力,具体取决于数据集的大小。目前,您的查询向服务器发送了一个请求,要求提供整个数据集(result.GetAll())。在您的系统上,它评估服务器返回的数据集上的 contains 部分 (result.where(contains).tolist() )。这样,服务器不会评估您的 contains 语句,因此服务器很满意。但是,您的服务器仍然需要做很多工作来评估此语句!

对于第二个问题:在实体框架中获取数据的标准方法是及时获取。这意味着,它仅在需要数据时评估查询,即数据转换为列表时、尝试循环遍历它或尝试打印它时。更早获得它的唯一方法是通过调用 result.Load() 而不是 .toList() 显式加载它。之后,你仍然可以调用 .toList(),但结果集是在你明确声明 .Load() 的那一刻加载的。

【讨论】:

你好迈克。那么,我不建议这样做吗?我可能有几百万个条目可供它搜索。如果可以,请建议我可以用来获取这些值的另一种解决方案?这些值检查用户的连接状态,分区键是配置文件 ID,行键是连接 ID。所以我需要一种方法来检查用户列表以查看谁在线!如果有分区键和连接 ID,那么它们基本上是在线的。我想我可以将所有这些存储在 Sql Server 中 确实,这样做可能会严重损害您的系统速度。老实说,我从来没有在 no-sql 环境中工作过。在我的环境中,我会尝试更改数据库,因此如果用户在线,我可以使用 where 语句来检查一个简单的布尔值。 .Where(onlineStatus == true) 之类的东西(当然非常简化)。

以上是关于在 Azure 表服务 linq 查询中运行包含运算符的主要内容,如果未能解决你的问题,请参考以下文章

Azure 表存储 - 表服务查询以检索并返回 10 个实体,直到最后一个实体

如何在 Linq 查询中多次包含一个表

LINQ查询表达式 - 查询表达式基础

Azure运维系列 3:安装和使用Azure PowerShell管理云

实体框架 LINQ - 具有分组依据的子查询

Postgres 选择查询在使用 JDBC 时运行缓慢,但在从同一服务器在 PSQL 中运行时快速