如何过滤 DbJoinExpression 的一侧
How do I Filter one side of a DbJoinExpression
在 EF 6.1 中创建了一个 DefaultExpressionVisitor 以与 IDbCommandTreeInterceptor 一起使用。我想知道如何正确覆盖 DbJoinExpression 访问者以过滤连接的右侧然后执行相同的连接但在过滤集上。
基于各种方法(例如使用 BindAs 等)我得到如下错误:
- 没有 属性 名称 'Extent2' 是由类型
声明的
- 引用的变量'Extent2'未在当前范围内定义。
但我无法获得可比较类型、变量和参数的组合。他们的文档很少,也没有在此上下文中使用 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;
}
在 EF 6.1 中创建了一个 DefaultExpressionVisitor 以与 IDbCommandTreeInterceptor 一起使用。我想知道如何正确覆盖 DbJoinExpression 访问者以过滤连接的右侧然后执行相同的连接但在过滤集上。
基于各种方法(例如使用 BindAs 等)我得到如下错误:
- 没有 属性 名称 'Extent2' 是由类型 声明的
- 引用的变量'Extent2'未在当前范围内定义。
但我无法获得可比较类型、变量和参数的组合。他们的文档很少,也没有在此上下文中使用 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;
}