【问题标题】:Generic Query With PredicateBuilder in LinqkitLinqkit 中使用 PredicateBuilder 的通用查询
【发布时间】:2017-09-16 03:01:51
【问题描述】:

我使用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 表达式 '(__property_0.Invoke([x]) == __filter_Value_1)' 无法翻译,将在当地进行评估。配置 此警告使用 DbContextOptionsBuilder.ConfigureWarnings API (事件 ID 'RelationalEventId.QueryClientEvaluationWarning')。 覆盖时可以使用 ConfigureWarnings DbContext.OnConfiguring 方法或使用 AddDbContext 上 应用服务提供商。

问题是:

我怎样才能使这个建筑工作? 另外,有什么最好的建议吗?

【问题讨论】:

    标签: c# entity-framework entity-framework-core linqkit


    【解决方案1】:

    你似乎忘记了,除了PredicateBuilder,LINQKit 提供的真正有用的功能是AsExpandableExpandInvoke 自定义扩展方法能够正确嵌入表达式在表达式树中。

    为了利用该功能,您应该使用Expression&lt;Func&lt;...&gt;&gt; 而不是Func&lt;...&gt;。在发布的代码中,将所有出现的Func&lt;TPredicate, string&gt; 替换为Expression&lt;Func&lt;TPredicate, string&gt;&gt;,问题应该得到解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-24
      • 2011-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-26
      • 1970-01-01
      相关资源
      最近更新 更多