Linqkit 中使用 PredicateBuilder 的通用查询
Generic Query With PredicateBuilder in 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's,我决定创建一个通用方法来将筛选器应用于属性。
我的想法是通过将应用过滤器的属性和过滤器定义,并封装表达式创建逻辑
它将是以下类型的东西:
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]
The LINQ expression '(__property_0.Invoke([x]) == __filter_Value_1)'
could not be translated and will be evaluated locally. To configure
this warning use the DbContextOptionsBuilder.ConfigureWarnings API
(event id 'RelationalEventId.QueryClientEvaluationWarning').
ConfigureWarnings can be used when overriding the
DbContext.OnConfiguring method or using AddDbContext on the
application service provider.
问题是:
我怎样才能使这个建筑工作?
另外,关于如何做到最好有什么建议吗?
你好像忘了,除了PredicateBuilder
,LINQKit AsExpandable
、Expand
和Invoke
自定义扩展方法提供的真正有用的功能是能够正确地将 expressions 嵌入到表达式树中。
为了利用该功能,您应该使用 Expression<Func<...>>
而不是 Func<...>
。在发布的代码中,将所有出现的 Func<TPredicate, string>
替换为 Expression<Func<TPredicate, string>>
,问题应该得到解决。
我使用 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's,我决定创建一个通用方法来将筛选器应用于属性。 我的想法是通过将应用过滤器的属性和过滤器定义,并封装表达式创建逻辑
它将是以下类型的东西:
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] The LINQ expression '(__property_0.Invoke([x]) == __filter_Value_1)' could not be translated and will be evaluated locally. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'RelationalEventId.QueryClientEvaluationWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider.
问题是:
我怎样才能使这个建筑工作? 另外,关于如何做到最好有什么建议吗?
你好像忘了,除了PredicateBuilder
,LINQKit AsExpandable
、Expand
和Invoke
自定义扩展方法提供的真正有用的功能是能够正确地将 expressions 嵌入到表达式树中。
为了利用该功能,您应该使用 Expression<Func<...>>
而不是 Func<...>
。在发布的代码中,将所有出现的 Func<TPredicate, string>
替换为 Expression<Func<TPredicate, string>>
,问题应该得到解决。