Linqkit中的PredicateBuilder通用查詢

c# entity-framework entity-framework-core linqkit

我一直在使用LinqKit來創建通用查詢。

一直困擾我的一件事是你總是要測試過濾器中發送的值是否有效。

例如:假設我有一個字符串過濾器。條件可以是Equal,StartsWith,EndsWith和Contains。

我的方法看起來像這樣:

public List<MyModel> Get(MyModelFilter filter)
{
    if (string.IsNullOrEmpty(filter.prop))
    {
        predicate = predicate.And(_myModel => myModel.Prop.Contains(filter.prop));
    }

    // Plus a giant amount of if's with multiple filters

    return DbSet.AsExpandable()
            .Where(predicate)
            .ToList();
}

為了結束這一堆If,我決定創建一個通用方法來將過濾器應用於屬性。我的想法是傳遞將應用過濾器的屬性和過濾器定義,並封裝表達式創建邏輯

它將是這種類型的東西:

public List<MyModel> Get(MyModelFilter filter)
{
    predicate = predicate.And(_myModel => myModel.Prop, filter.PropFilterDefinition);

    // Goodnye If's, Only others filter impl

    return DbSet.AsExpandable()
            .Where(predicate)
            .ToList();
}

為此,我創建了一些擴展方法來處理這個問題

public static Expression<Func<TPredicate, bool>> And<TPredicate>(
    this ExpressionStarter<TPredicate> predicate,
    Func<TPredicate, string> property, StringFilterDefinition filter,
    bool ignoreNull = true)
{
    if (InvalidStringFilter(filter, ignoreNull))
    {
        return predicate;
    }

    // This is LinqKit's And Extension Method
    return predicate.And(BuildPredicate(property, filter));
}

private static Expression<Func<TPredicate, bool>> BuildPredicate<TPredicate>(
    Func<TPredicate, string> property,
    StringFilterDefinition filter)
{
    if (filter.Filter == StringFilterComparators.Equal)
    {
        return x => property.Invoke(x) == filter.Value;
    }

    if (filter.Filter == StringFilterComparators.BeginsWith)
    {
        return x => property.Invoke(x).StartsWith(filter.Value);
    }

    if (filter.Filter == StringFilterComparators.EndsWith)
    {
        return x => property.Invoke(x).EndsWith(filter.Value);
    }

    return x => property.Invoke(x).Contains(filter.Value);
}

private static bool InvalidStringFilter(
    StringFilterDefinition filter, 
    bool ignoreNullValue = true)
{
    if (filter?.Filter == null)
    {
        return true;
    }

    return ignoreNullValue && string.IsNullOrEmpty(filter.Value);
}

問題是沒有應用過濾器,答案就在Invoke中。 EF無法將上述表達式轉換為SQL。 EF錯誤是

Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory [8] LINQ表達式'(__ propertyperty_0.Invoke([x])== __filter_Value_1)'無法翻譯,將在本地進行評估。要配置此警告,請使用DbContextOptionsBuilder.ConfigureWarnings API(事件ID“RelationalEventId.QueryClientEvaluationWarning”)。重寫DbContext.OnConfiguring方法或在應用程序服務提供程序上使用AddDbContext時,可以使用ConfigureWarnings。

問題是:

我怎樣才能使這個建築工作?另外,關於如何最好的任何建議?

一般承認的答案

您似乎忘記了除了PredicateBuilder之外,LINQKit AsExpandable提供的非常有用的功能, ExpandInvoke自定義擴展方法是能夠在表達式樹中正確嵌入表達式

要使用該功能,您應該使用Expression<Func<...>>而不是Func<...> 。在已發布的代碼中,將所有出現的Func<TPredicate, string>替換為Expression<Func<TPredicate, string>> ,問題應該解決。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow