2

在EF 6.1中,已創建了一個DefaultExpressionVisitor以與IDbCommandTreeInterceptor配合使用。我想知道如何正確覆蓋DbJoinExpression訪問者以過濾連接的右側,然後執行相同的連接,但在過濾的集合上。如何過濾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'***) 

source code on codeplex它是推動範圍變量DefaultExpressionVisitor但這種方法是私有的。這可能解釋我看到的參數範圍問題。

任何幫助,將不勝感激。

+0

我有類似的問題。也許我的回答可以幫助你:http://stackoverflow.com/a/34187228/1876903, – Killo 2015-12-09 19:25:27

回答

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; 
    }