14

我試圖使用實體框架CommandTree攔截器通過DbContext爲每個查詢添加一個過濾器。爲了簡單起見,我有兩個表,一個名爲「User」,兩列(「UserId」和「EmailAddress」),另一個名爲「TenantUser」,兩列(「UserId」和「TenantId」) 。在實體框架攔截器中添加內部連接到DbScanExpression

每次有用戶表的DbScan時,我想根據TenantId列對TenantUser表進行內部聯接並進行篩選。

有一個名爲EntityFramework.Filters的項目,它沿着這些線做了一些事情,但不支持「複雜連接」,這似乎是我正在嘗試做的。

a demo from TechEd 2014後,我創建了一個攔截器,它使用訪問器並使用下面的方法用DbJoinExpression替換DbScanExpressions。一旦我得到這個工作,我打算將它包裝在一個DbFilterExpression中,以將TenantId列與已知ID進行比較。

public override DbExpression Visit(DbScanExpression expression) 
    { 
     var table = expression.Target.ElementType as EntityType; 
     if (table != null && table.Name == "User") 
     { 
      return DbExpressionBuilder.InnerJoin(expression, DbExpressionBuilder.Scan(expression.Target), (l, r) => 
       DbExpressionBuilder.Equal(DbExpressionBuilder.Variable(tenantUserIdProperty.TypeUsage, "UserId"), 
        DbExpressionBuilder.Variable(userIdProperty.TypeUsage, "UserId"))); 
     } 

     return base.Visit(expression); 
    } 

爲了測試上面的代碼,我已經添加了攔截到的DbContext和運行下面的代碼:

dbContext.Users.Select(u => new { u.EmailAddress }).ToList(); 

然而,這會導致以下錯誤:

No property with the name 'EmailAddress' is declared by the type 'Transient.rowtype[(l,CodeFirstDatabaseSchema.User(Nullable=True,DefaultValue=)),(r,CodeFirstDatabaseSchema.User(Nullable=True,DefaultValue=))]'.

我是不是正確構建了DbJoinExpression?還是我錯過了別的?

回答

3

你得到這個異常的原因是因爲InnerJoin產生一個結合兩個表的列的結果,另一方面查詢應該返回類User的那些匹配屬性,所以你還需要在末尾使用投影的查詢。這裏是爲我工作的代碼:

public override DbExpression Visit(DbScanExpression expression) 
{ 
    var table = expression.Target.ElementType as EntityType; 
    if (table != null && table.Name == "User") 
    { 
     return expression.InnerJoin(
      DbExpressionBuilder.Scan(expression.Target.EntityContainer.BaseEntitySets.Single(s => s.Name == "TennantUser")), 
      (l, r) => 
       DbExpressionBuilder.Equal(
        DbExpressionBuilder.Property(l, "UserId"), 
        DbExpressionBuilder.Property(r, "UserId") 
       ) 
     ) 
     .Select(exp => 
      new { 
       UserId = exp.Property("l").Property("UserId"), 
       Email = exp.Property("l").Property("Email") 
      }); 
    } 

    return base.Visit(expression); 
} 

正如你看到的,通過使用它的lambda表達式的別名從表達式,指定連接條件你是指特定的連接表操作之後。所以在我的情況下,您將用戶表稱爲l並將TennantUser稱爲r。將使用字母l和r以及發送到數據庫的結果SQL查詢中的別名。在InnerJoin和Select操作之間,您可能需要額外的邏輯,如濾波器等。

+0

這正是我所需要的。非常感謝! – 2015-04-09 16:22:16

+0

我的回答很可能對你來說太晚了,對不起,但對未來有些人可能會使用它。 – mr100 2015-04-09 16:23:35

+0

有沒有辦法將正則表達式傳遞給訪問者以使這些掃描的寫入更容易? – AndrewP 2016-01-04 09:40:35