【问题标题】:How-to use predicates for customer where LINQ query in Entity Framework, Extension Method "Search by Words"如何在实体框架中使用 LINQ 查询的客户的谓词,扩展方法“按单词搜索”
【发布时间】:2016-07-05 14:07:54
【问题描述】:

我想使用谓词构建自定义 linq 查询。

而不是使用静态的Where 子句,即...

public IEnumerable<Entities.BusinessDirectory.BDEntity> Search(string searchExpression)
{
    var db = new ApplicationDbContext();
    return db.BDEntities
             .Where(x => searchExpression.Split(' ').Any(y => x.Value.Contains(y)));
}

...我正在使用泛型方法Search(predicate, searchExpression) 扩展DbContext,它应该具有与静态方法相同的效果(见上文)

public static class DbContextExtensions
{
    public static IEnumerable<T> Search<T>(
        this DbSet<T> dbSet, 
        Expression<Func<T, object>> predicate, 
        string searchExpression)
      where T : class, new()
    {
        //Experimental
        return dbSet
            .Where(x => searchExpression.Split(' ').Any(y => predicate.Contains(x)));
    }
}

但是,我收到以下错误:

错误 6 实例参数:无法从 'System.Linq.Expressions.Expression>' 到 'System.Linq.ParallelQuery' Extensions.cs

【问题讨论】:

  • predicate 代表什么?请添加一个示例,说明如何使用此方法。
  • 您没有释放数据库上下文db。但是您肯定会在您的实际应用程序中这样做,对吗?你是吗?

标签: linq search where predicate


【解决方案1】:

简单的答案是,您可以只在查询Where 子句中添加谓词,请记住Where 谓词的定义是它返回bool。最终,您的表达式必须是 boolean 表达式才能添加到 Where 子句中。

Expression<Func<BDEntity, bool>> predicate = (BDEntity entity) => entity.Field == "TEST";
var query = context.BDEntities;

if (predicate != null)
    query = query.Where(predicate);

// expand to get the results.
var results = query.ToList();

动态表达

如果您需要在运行时构建像 _ =&gt; _.MyFieldValue == "TEST" 这样的表达式,那就有点棘手了。

给定一个谓词_ =&gt; _.DatabaseField 和一个字符串值"TEST",创建一个适合的表达式 实体框架来创建一个测试谓词的where 子句。

此方法将构建一个适当的Equal 表达式。

public static Expression<Func<T, bool>> BuildEqualPredicate<T>(
    Expression<Func<T, string>> memberAccessor,
    string term)
{
    var toString = Expression.Convert(Expression.Constant(term), typeof(string));
    Expression expression = Expression.Equal(memberAccessor.Body, toString);
    var predicate = Expression.Lambda<Func<T, bool>>(
        expression,
        memberAccessor.Parameters);
    return predicate;
}

以下断言将通过:

var expression = Predicate.BuildEqualPredicate<MyDbEntity>(_ => _.DatabaseField, "TEST");
expression.Compile()
    .Invoke(new MyDbEntity { DatabaseField = "TEST" })
    .Should().BeTrue();
expression.Compile()
    .Invoke(new MyDbEntity { DatabaseField = "ANOTHERVALUE"})
    .Should().BeFalse();

通过将谓词包含在 where 子句中,实体框架可以使用谓词。

var predicate = PredicateExtensions.BuildEqualPredicate<MyDbEntity>(
    _ => _.DatabaseField,
    "TEST");
var results = context.DbEntity.Where(predicate).ToList();

另外

您可以使用成员表达式做更多事情,例如使用StartsWithContains 创建更复杂的谓词。

Expression<Func<T, string>> memberAccessor = _ => _.DBEntityField;

您可以像这样动态创建谓词,在本例中,它创建一个谓词来检查字符串 StartsWith 是否为特定值 or is null

private Expression<Func<T, bool>> BuildPredicate(
    string term,
    Expression<Func<T, string>> memberAccessor)
{
    var startsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
    var isNull = typeof(string).GetMethod("IsNullOrEmpty", new[] { typeof(string) });
    var toString = Expression.Convert(Expression.Constant(term), typeof(string));
    Expression expression = Expression.Call(
        memberAccessor.Body,
        startsWith,
        new Expression[] { toString });
    expression = Expression.Or(expression, Expression.Call(
        isNull,
        toString));
    var predicate = Expression.Lambda<Func<T, bool>>(
        expression,
        memberAccessor.Parameters);
    return predicate;
}

这将为您提供一个计算结果如下的表达式:

Expression<Func<BDEntity, bool>> expression = _ => 
   string.IsNullOrEmpty(term)
   || _.DBEntityField.StartsWith(term)

这个表达式有几个优点:

  • 可以转换成合适的SQL
  • termnullempty 时返回所有记录
  • StartsWith 确保可以使用适当的索引

expression 可以类似地使用 query = query.Where(expression) 添加到查询中,将 StartsWith 替换为 Contains 将搜索字符串中的任何匹配项,但您将放弃任何 SQL 索引优化,所以是禁忌的。

【讨论】:

  • 您好@Jim,我认为您为我指明了正确的方向。但是,我仍然无法通过使用定义 Property == searchExpression 的谓词 ('Expression> predicate') 来建立继承的 DbContext 类的扩展方法。如何通过结合谓词和字符串来构建 Expression>?
  • 我现在正在更新答案。
  • 很好的答案。我还要补充一点,您需要小心 Linq 表达式中的捕获。例如searchExpression.Split(' ') 将捕获seachExpression,然后调用string.Split,这不是EF 可调用的。
  • Jim - 以您为例:可以使用 T 而不是 BDEntity 类,即 Expression&lt;Func&lt;T, bool&gt;&gt; predicate = (T entity) =&gt; {field} == searchExpression; 而 {field} 是函数参数中的谓词 Expression>?
  • 您可以为 {T} 使用任何占位符。泛型类型需要解析为特定的类或接口才能具有特定的属性。给定:(T entity) =&gt; entity.SomeField == searchExpression,T 需要是具有名为SomeField 的属性的接口或类。在这种情况下,我通常做的是确保每个具有属性SomeField 的实体框架实体都继承一个特定的接口。 ISearchable 例如。所有ISearchable 实体都将拥有SomeField 属性。然后输入 {T} where T : ISearchable
猜你喜欢
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 2018-06-29
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多