2017-04-19 116 views
0

我一直在使用LinqKit来创建通用查询很长一段时间。在Linqkit中使用PredicateBuilder进行通用查询

总是困扰着我的一件事是,你总是必须测试过滤器中发送的值是否有效。

例如:假设我有一个字符串过滤器。条件可以是平等的,开始的,结束的,包含的。

我的方法会是这个样子:

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(); 
} 

要结束这一堆如果的,我决定创建的过滤器适用于性能的通用方法。 我的想法是通过在过滤器将被应用的属性和过滤器定义,并封装表达创建逻辑

这将是类型的东西:

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); 
} 

问题是过滤器没有被应用,并且答案是在那里调用。 EF无法将上述表达式转换为SQL。 的EF误差是

Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory [8]所述的 LINQ表达式 '(__property_0.Invoke([X])== __filter_Value_1)' 无法翻译和将本地评估。要配置 ,此警告使用DbContextOptionsBuilder.ConfigureWarnings API (事件ID'RelationalEventId.QueryClientEvaluationWarning')。 重写 DbContext.OnConfiguring方法或使用 应用程序服务提供者上的AddDbContext时,可以使用ConfigureWarnings。

的问题是:

我怎样才能使这项工程的? 另外,关于如何最好的这个建议?

回答

4

你似乎忘了,除了PredicateBuilder,通过LINQKit AsExpandableExpandInvoke自定义扩展方法提供了非常有用的功能是能够正确地嵌入表达表达式树内。

为了利用该功能,你应该使用Expression<Func<...>>,而不是Func<...>。在发布的代码中,将所有发生的Func<TPredicate, string>替换为Expression<Func<TPredicate, string>>,并且问题应该解决。