如何过滤DbJoinExpression的一侧

时间:2015-04-01 01:09:07

标签: c# entity-framework expression-trees entity-framework-6.1

在EF 6.1中创建了一个与IDbCommandTreeInterceptor一起使用的DefaultExpressionVisitor。我想知道如何正确覆盖DbJoinExpression访问者以过滤连接的右侧,然后在过滤集上执行相同的连接。

基于各种方法(例如使用BindAs等),我得到错误,例如:

  • 类型
  • 未声明名称为“Extent2”的属性
  • 引用的变量'Extent2'未在当前范围中定义。

但我无法得到可比类型,变量和参数的混合。在这种情况下,它们几乎没有文档,也没有DbJoinExpressions用法的例子。

作为一个例子,我说有一个带有人和动物的ObjectContext。 并且一个人拥有他们拥有的动物协会,而宠物拥有一个OwnerId。所以显式Key关系在Person.Id == Animal.OwnerId。

之间

我添加了一个关联,也是一个导航属性,并称之为“猫”。

因此,为了准确起见,我想使用AnimalType列作为鉴别器来过滤动物集合(右手表达式)。

    public override DbExpression Visit(DbJoinExpression expression)
    {
       //TODO pull these values from attributes etc
        var discriminatorColumn = "AnimalType";
        var discriminatorType = "Cat";

        //People
        DbExpressionBinding left = this.VisitExpressionBinding(expression.Left);
        //Unfiltered Animals
        DbExpressionBinding right = this.VisitExpressionBinding(expression.Right);


        //TODO Filter the right side using the AnimalType dbcolumn and re-join
        // Get the right hand collection element
        var entitySetExpression = right.Expression as DbScanExpression;

        var variableReference = right.Variable;

        // Create the property based on the variable in order to apply the equality
        var discriminatorProperty = DbExpressionBuilder.Property(variableReference, discriminatorColumn);
        var predicateExpression = DbExpressionBuilder.Equal(discriminatorProperty, DbExpression.FromString(discriminatorType));

        //Filtered Animals being Cats
        var filterExpression = DbExpressionBuilder.Filter(entitySetExpression.Bind(),predicateExpression);


        var joinCondition = this.VisitExpression(expression.JoinCondition) as DbComparisonExpression;
        DbExpressionBinding filteredRight = filterExpression.Bind();

        DbExpression newExpression = expression;
        if (!ReferenceEquals(expression.Left, left)
            || !ReferenceEquals(expression.Right, filteredRight)
            || !ReferenceEquals(expression.JoinCondition, joinCondition))
        {
            if (DbExpressionKind.InnerJoin == expression.ExpressionKind)
            {
                newExpression = DbExpressionBuilder.InnerJoin(left, filteredRight, joinCondition);
            }
            else if (DbExpressionKind.LeftOuterJoin == expression.ExpressionKind)
            {
                newExpression = DbExpressionBuilder.LeftOuterJoin(left, filteredRight, joinCondition);
            }
            else
            {
                Debug.Assert(
                    expression.ExpressionKind == DbExpressionKind.FullOuterJoin,
                    "DbJoinExpression had ExpressionKind other than InnerJoin, LeftOuterJoin or FullOuterJoin?");
                newExpression = DbExpressionBuilder.FullOuterJoin(left, filteredRight, joinCondition);
            }
        }

        return newExpression;
    }

基本上我想创建一个带有额外过滤器的SQL连接,如:

SELECT ....
FROM People p LEFT JOIN
     Animals a ON p.Id = a.OwnerId (here ***AND a.AnimalType = 'Cat'***)
WHERE ( or here ***a.AnimalType = 'Cat'***)

为DefaultExpressionVisitor读取source code on codeplex它正在推送范围变量,但此方法是私有的。这可能解释了我所看到的参数范围问题。

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

事实证明,我认为更简单。 我避免尝试过滤DbScanExpression并简单地使用AndExpression为连接添加了另一个条件

    public override DbExpression Visit(DbJoinExpression expression)
    {
        //TODO pull these values from attributes etc
        var discriminatorColumn = "AnimalType";
        var discriminatorType = "Cat";
        //if (Attribute.GetCustomAttributes())

        //People
        DbExpressionBinding left = this.VisitExpressionBinding(expression.Left);
        //Unfiltered Animals
        DbExpressionBinding right = this.VisitExpressionBinding(expression.Right);



        // Create the property based on the variable in order to apply the equality
        var discriminatorProperty = DbExpressionBuilder.Property(right.Variable, discriminatorColumn);

        //TODO create type from discriminatorType to match property type eg string, guid, int etc
        var predicateExpression = DbExpressionBuilder.Equal(discriminatorProperty, DbExpression.FromString(discriminatorType));

        //Use existing condition and combine with new condition using And
        var joinCondition = DbExpressionBuilder.And(expression.JoinCondition, predicateExpression);



        DbExpression newExpression = expression;

        //only re-create the join if something changed
        if (!ReferenceEquals(expression.Left, left)
            || !ReferenceEquals(expression.Right, right)
            || !ReferenceEquals(expression.JoinCondition, joinCondition))
        {
            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.InnerJoin:
                    newExpression = DbExpressionBuilder.InnerJoin(left, right, joinCondition);
                    break;
                case DbExpressionKind.LeftOuterJoin:
                    newExpression = DbExpressionBuilder.LeftOuterJoin(left, right, joinCondition);
                    break;
                default:
                    Debug.Assert(
                        expression.ExpressionKind == DbExpressionKind.FullOuterJoin,
                        "DbJoinExpression had ExpressionKind other than InnerJoin, LeftOuterJoin or FullOuterJoin?");
                    newExpression = DbExpressionBuilder.FullOuterJoin(left, right, joinCondition);
                    break;
            }
        }

        return newExpression;
    }