如何过滤 DbJoinExpression 的一侧

How do I Filter one side of a DbJoinExpression

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

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

但我无法获得可比较类型、变量和参数的组合。他们的文档很少,也没有在此上下文中使用 DbJoinExpressions 的示例。

举个例子,假设我有一个包含人和动物的 ObjectContext。 Person 与他们拥有的 Animals 有关联,Pet 有 OwnerId。所以明确的Key关系是Person.Id == Animal.OwnerId.

我添加了一个关联,还有一个导航 属性 并将其命名为 "Cats"。

为了准确起见,我想使用 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 它正在推送范围变量,但此方法是私有的。这可能解释了我所看到的参数范围问题。

如有任何帮助,我们将不胜感激。

原来比我想的要简单。 我避免尝试过滤 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;
    }