EF不在DB级别执行where子句

时间:2012-10-11 19:16:56

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

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

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

假设这两个类:

  1. MyEntity(项目中DataContract命名空间的一部分)
  2. MyEntity(项目中Entities命名空间的一部分)
  3. 两者都具有相同的属性,我使用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+),我有ZERO输入在所述数据库上。对于可能正在使用我正在编写的客户端的其他开发人员来说,使用任何数据创建表达式都会轻松得多。他们想要的列,所以我喜欢来应用db级别的位置。

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

    谢谢!

    / walloftext

3 个答案:

答案 0 :(得分:2)

问题可能在于这一行:

var func = entityExpression.Compile();

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

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

答案 1 :(得分:1)

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

换句话说,请尝试删除此行:

var func = entityExpression.Compile();

答案 2 :(得分:0)

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

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