【问题标题】:EF not executing where clause at DB levelEF 未在数据库级别执行 where 子句
【发布时间】:2012-10-12 03:28:29
【问题描述】:

免责声明:我将服务/实体/等的名称更改为记事本中 SO 的通用名称。如果您发现类名不一致,请忽略它们,因为这不是问题。

我正在为客户开发 WCF 服务,但我正在序列化的表达式存在一些问题。我目前正在使用Serialize.Linq 来处理表达式序列化。最重要的是,我使用我的 DataContract 类在客户端创建表达式,并使用我的 Entity 类将其转换为表达式。

假设这两个类:

  1. MyEntity(项目中 DataContract 命名空间的一部分)
  2. MyEntity(项目中实体命名空间的一部分)

两者具有相同的属性,我使用 AutoMapper 将通过 EF 获得的实体转换为我的 DataContract 对象,然后将该对象发送回客户端。

为了处理表达式转换,我使用了一个 ExpressionVisitor 类:

class MyExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression ParameterExpression { get; private set; }

    public MyExpressionVisitor(ParameterExpression newParameterExp)
    {
        ParameterExpression = newParameterExp;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return ParameterExpression;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.DeclaringType == typeof(DataContracts.MyEntity))
        {
            return Expression
                .MakeMemberAccess(this.Visit(node.Expression), typeof(Entities.MyEntity).GetMember(node.Member.Name).FirstOrDefault());
        }

        return base.VisitMember(node);
    }
}

这就是我调用服务的方式:

Expression<Func<DataContracts.MyEntity, bool>> expression =
    fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe";
var entities = manager.MyService.GetFilteredEntities(expression.ToExpressionNode());

这是我当前为GetFilteredEntities 实现的服务(部分、省略返回行等):

// Using Serialize.Linq for send expressions over WCF.
// query is an ExpressionNode from the Serialize.Linq library.
// This DOES NOT execute the where clause on the database.
var expression = query.ToExpression<Func<DataContracts.MyEntity, bool>>();
var visitor = new MyExpressionVisitor(Expression.Parameter(typeof(Entities.MyEntity), expression.Parameters[0].Name));
var entityExpression = Expression.Lambda<Func<Entities.MyEntity, bool>>(visitor.Visit(expression.Body), visitor.ParameterExpression);
var func = entityExpression.Compile();
var entities = this.Entities.MyEntities.Where(func);

所有这些代码有效,但Where 并未应用于数据库级别,它应用于我表中每一行的内存集.这需要很长时间,因为该表有 150k+ 行。

如果我在服务中硬编码我想要的位置,它会在数据库级别应用 where 子句:

// This DOES execute the where clause on the database.
var temp1 = this.Entities.MyEntities.Where(fl => fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe");

// This DOES execute the where clause on the database.
Func<Entities.MyEntity, bool> func2 = fl => fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe";
var temp2 = this.Entities.MyEntities.Where(func2);

我知道我可以编写一堆不同的服务操作,允许用户通过传入名称、ID 等进行过滤,但是这个表有大量的列(200+),而且我有零输入在所述数据库上。对于可能正在使用我正在编写的客户端的其他开发人员来说,能够使用他们喜欢的任何数据和列来创建表达式要容易得多,所以我喜欢将这个应用到在 db 级别的位置。

我几乎可以肯定我已经在这篇文章中包含了所有相关的内容。我使用 SQL Server Profiler 检查 EF 正在运行的查询,这就是我知道哪些查询在哪里使用的方式。如果需要,请在哪里/如果需要更多信息。

谢谢!

/walloftext

【问题讨论】:

标签: c# .net entity-framework-4


【解决方案1】:

也许问题出在这一行:

var func = entityExpression.Compile();

由于您使用已编译的委托函数调用 Where,因此您没有调用实际发送到数据库的 Where 的重载。

您需要将表达式对象传递给Where(即调用one of these

【讨论】:

  • 呃,就是这样。我以前没有像这样处理过表达式树,而是遵循Serialize.Linq 的示例。说真的,非常感谢你!已验证删除它并仅调用.Where(entityExpression)。只要允许我,就会将此标记为答案。
【解决方案2】:

我认为在 Where 子句中使用表达式之前不应该编译表达式。这样做将调用System.Linq 命名空间在System.Core 中提供的扩展方法。您需要采用Expression 类型对象的方法重载。

换句话说,尝试删除这一行:

var func = entityExpression.Compile();

【讨论】:

    【解决方案3】:

    您是否尝试过使用LinqKit 创建复合查询?它通过PredicateBuilder 提供了一种使用表达式的简单方法。

    看看this address,源代码很简单,在我的案例中创造了奇迹。

    【讨论】:

    • 我喜欢PredicateBuilder,但据我所知,这在这里无济于事,主要是因为我必须通过 WCF 进行序列化。
    猜你喜欢
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-06
    • 2012-06-08
    相关资源
    最近更新 更多