在EF 6.1中创建了一个与IDbCommandTreeInterceptor一起使用的DefaultExpressionVisitor。我想知道如何正确覆盖DbJoinExpression访问者以过滤连接的右侧,然后在过滤集上执行相同的连接。
基于各种方法(例如使用BindAs等),我得到错误,例如:
但我无法得到可比类型,变量和参数的混合。在这种情况下,它们几乎没有文档,也没有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它正在推送范围变量,但此方法是私有的。这可能解释了我所看到的参数范围问题。
任何帮助将不胜感激。
答案 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;
}