簡體   English   中英

在 Azure 表服務 linq 查詢中運行包含運算符

[英]Running a Contains operator in Azure Table Service linq query

我想知道為什么/如何在 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 個條件。

但如果你知道,計數不是那么多,你可以這樣做。

我編寫了一個擴展方法來動態地在 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()是創建和執行查詢的擴展方法

如您提供的站點所述,Azure 表服務不支持包含語句的驗證。 由於數據保存在無后續環境中,因此包含語句可能需要大量功率,具體取決於數據集的大小。 目前,您的查詢向服務器發送請求,要求提供整個數據集 (result.GetAll())。 在您的系統上,它評估服務器返回的數據集上的 contains 部分(result.where(contains).tolist())。 這樣,服務器不會評估您的 contains 語句,因此服務器很滿意。 但是,您的服務器仍然需要做很多工作來評估此語句!

對於您的第二個問題:在實體框架中獲取數據的標准方法是及時獲取。 這意味着,它只在需要數據的那一刻評估查詢,也就是在數據轉換為列表的那一刻,當您嘗試遍歷它時,或者當您嘗試打印它時。 提前獲取它的唯一方法是通過調用 result.Load() 而不是 .toList() 顯式加載它。 之后,您仍然可以調用 .toList(),但結果集是在您明確聲明 .Load() 的那一刻加載的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM