14

Próbuję użyć przechwytywacza Entity Framework CommandTree, aby dodać filtr do każdego zapytania za pomocą DbContext.Dodawanie wewnętrznego sprzężenia do DbScanExpression w Entity Framework Interceptor

Dla uproszczenia, mam dwie tabele, jedną o nazwie "Użytkownik" z dwiema kolumnami ("UserId" i "EmailAddress") i drugą nazwaną "TenantUser" z dwiema kolumnami ("UserId" i "TenantId") .

Za każdym razem, gdy istnieje tabela DbScan użytkownika, chcę wykonać sprzężenie wewnętrzne względem tabeli TenantUser i filtrować na podstawie kolumny TenantId.

Istnieje projekt o nazwie EntityFramework.Filters, który robi coś podobnego, ale nie obsługuje "złożonych połączeń", co wydaje się być tym, co próbuję zrobić.

Po a demo from TechEd 2014, stworzyłem przechwytywacz, który używa gościa z poniższą metodą, aby zastąpić wyrażenie DbScanExpressions wyrażeniem DbJoin. Kiedy już to zrobię, zamierzam owijać go w DbFilterExpression, aby porównać kolumnę TenantId ze znanym 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); 
    } 

Aby przetestować powyższy kod, dodałem do przechwytywania dbContext i uruchom następujący kod:

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

Powoduje to jednak w następujący błąd:

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=))]'.

Czy niepoprawnie buduję wyrażenie DbJoin? Czy może brakuje mi czegoś innego?

Odpowiedz

3

Powód, dla którego uzyskano ten wyjątek, polega na tym, że InnerJoin tworzy wynik połączony kolumnami z obu tabel, az drugiej strony zapytanie ma zwrócić te pasujące właściwości klasy Użytkownik, więc dodatkowo trzeba użyć rzutowania na końcu zapytania. Oto kod, który pracował dla mnie:

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

jak widać po dołączyć operację odwołać się do konkretnej tabeli połączonej za pomocą wyrażenia lambda jego alias z wyrażeniem określającym dołączyć warunek. Więc w moim przypadku odwołujesz się do tabeli użytkowników jako l i do TennantUser jako r. Używane będą litery l i r, a także aliasy w wynikowym zapytaniu SQL wysyłanym do bazy danych. Pomiędzy operacjami InnerJoin i Select możesz umieścić dodatkową logikę, której potrzebujesz, np. Filter itp.

+0

To jest dokładnie to, czego potrzebowałem. Dziękuję bardzo! –

+0

Moja odpowiedź nadeszła prawdopodobnie "trochę" za późno, przepraszam za to :) Ale może w przyszłości ktoś będzie z niej korzystał. – mr100

+0

Czy jest jakiś sposób przekazania zwykłego Wyrażania w Gość, aby ułatwić pisanie tych skanów? – AndrewP

Powiązane problemy