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



我想知道为什么/如何在Azure存储表上运行此查询,因为"包含"在Azure表服务中不允许?这不是在做我认为它在做的事情吗?它正在运行和获取值。另外,这是不是先获取整个表,然后进行筛选?在调试器中,它看起来直到我运行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();

我知道这是一篇旧帖子,但我们确实遇到了类似的问题,我没有发现更新的内容。

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

因此,在查询中最简单的方法是创建一个倍数或条件。将其更改为类似new TableQuery<ConnectionEntity>().Where(w => w.PartitionKey == "1" || w.PartitionKey == "2" || ...)的内容。

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

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

我写了一个扩展方法来在像.Contains()这样的IQueryable动态上实现这一点(用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()是创建和执行查询的扩展方法

正如您提供的网站上所述,Azure表服务不支持对包含语句的验证。由于数据保存在无SQL环境中,根据数据集的大小,contains语句可能需要巨大的功率。此时,您的查询会向服务器发送一个请求,请求整个数据集(result.GetAll(((。在您的系统上,它评估服务器返回的数据集上的包含部分(result.where(contains(.tolist(((。这样,服务器就不会评估您的contains语句,所以服务器是满意的。然而,您的服务器仍然需要做大量的工作来评估此语句!

对于您的第二个问题:在实体框架中获取数据的标准方法是及时获取数据。这意味着,它只在需要数据的时候评估查询,也就是数据转换为列表的时候,当你试图循环它的时候,或者当你试图打印它的时候。早期获取它的唯一方法是通过调用result显式加载它。Load((而不是.toList((。之后,您仍然可以调用.toList

相关内容

最新更新