Как отфильтровать одну сторону выражения DbJoinExpression

В EF 6.1 создан DefaultExpressionVisitor для использования с IDbCommandTreeInterceptor. Я хочу знать, как правильно переопределить посетителя DbJoinExpression, чтобы отфильтровать правую часть соединения, а затем выполнить то же соединение, но в отфильтрованном наборе.

Основываясь на различных подходах (например, с использованием BindAs и т. д.), я получаю такие ошибки, как:

  • Никакое свойство с именем «Extent2» не объявлено типом
  • Указанная переменная «Extent2» не определена в текущей области.

но я не могу получить сочетание сопоставимых типов, переменных и параметров. У них мало документации и нет примеров использования DbJoinExpressions в этом контексте.

В качестве примера скажем, что у меня есть ObjectContext с людьми и животными. И у человека есть ассоциация с животными, которыми он владеет, а у домашнего животного есть OwnerId. Таким образом, явное ключевое отношение находится между 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'***)

Чтение исходного кода на codeplex для DefaultExpressionVisitor отправляет переменные области видимости, но этот метод является закрытым. Это может объяснить проблемы с областью действия параметров, которые я вижу.

Любая помощь будет оценена по достоинству.


person Simon Francesco    schedule 01.04.2015    source источник
comment
У меня была аналогичная проблема. Возможно, мой ответ поможет вам: stackoverflow.com/a/34187228/1876903,   -  person Killo    schedule 09.12.2015


Ответы (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;
    }
person Simon Francesco    schedule 13.11.2016