2012-12-06 15 views
6

Przeczytałem około 30 pytań przepełnienia stosu i około 20 blogów i nie mogę znaleźć odpowiedzi. To powiedziawszy, jestem pewien, że odpowiedź, której wymagam, jest tam dostępna, więc jeśli wiesz o tym, powiedz to (proszę zauważyć ostatni akapit/zdanie dotyczące odpowiedzi, które nie pasują do mojego wymogu). Dzięki!Wyrażenie <Func <TEntity, bool >> zagnieżdżanie, które działa z Entity Framework

Jestem utrzymujących instancje klas, które mają podobny kształt do:

public class Data { 
    public int Id { get; set; } 
} 

public class Container { 
    public int Id { get; set; } 
    public Data Data { get; set; } 
} 

Obecnie zapytania są zapisywane znaleźć Kontenery przez warstwę abstrakcji, która wymaga wyrażenia lambda przyjmującą pojemnik i powracającego bool (źródłowe). Nie zaakceptuje IQueryable, mimo że Entity Framework 5 jest ORM z wyboru.

Moim zadaniem jest przedstawienie powierzchni API opartej na wyrażeń Lambda, które akceptują typ danych. Nie mogę zmienić warstwę abstrakcji (muszę przekazać predykat, który akceptuje pojemniku), więc staram się przekształcić wyrażenie otrzymam jako:

Expression<Func<Data , bool>> 
to: 
Expression<Func<Container , bool>> 

dodałem dodatkową metodę w moim repozytorium klasy, takie jak:

public Container Find(Expression<Func<Data , bool>> predicate) { 

    IEnumerable<Container> result = QueryStrategy.Fetch(c => predicate.Compile().Invoke(c.Data)); 

    return result.FirstOrDefault(); 

} 

ten uzupełnia istniejące metody znajdują się:

public Container Find(Expression<Func<Container , bool>> predicate) { 

    IEnumerable<Container> result = QueryStrategy.Fetch(predicate); 

    return result.FirstOrDefault(); 

} 

Kiedy dawny metoda jest wykorzystywana produkuje następujący wyjątek:

LINQ jednostki nie zna metody „Boole'a Invoke (Container.Data) metody”, i ten sposób nie może być przekłada się na ekspresji w sklepie.

Próbowałem różne rzeczy z klas Ekspresji ja po prostu nie widzę sposobu na mapie:

Expression<Func<Data , bool>> 
to: 
Expression<Func<Container , bool>> 

Bez użycia Invoke, który nie jest obsługiwany przez Entity Framework (ale działa z opcją w pamięć przeliczalne dane).

Czy ktoś może mi pomóc uzyskać powyższy scenariusz przy użyciu wyrażeń?

Rozumiem, że być może uda mi się skorzystać z biblioteki LinqKit, aby rozwiązać ten problem, ale naprawdę chcę rozwiązać ten problem, nie wprowadzając biblioteki stron trzecich.

AKTUALIZACJA: Starając się przedstawić uproszczony problem, sugerowałem (początkowo używając DbContext w przykładowym kodzie), że kod będzie miał dostęp do IQueryable i/lub będzie odpowiedni do używania AsExpandable LinqKit(). W rzeczywistości tak nie jest - klasa repozytorium nie może używać rozszerzeń specyficznych dla IQueryable lub jakichkolwiek dostawców. Zmodyfiowałem powyższe przykłady i mam nadzieję, że dzięki temu sprawy staną się jaśniejsze.

Ten problem ten rozwiązano

+0

Czy wymagane jest filtrowanie kontenerów przez twoje wyrażenie Func ma miejsce w bazie danych? –

+0

Dobre pytanie - tak, jest. – FantasticJamieBurns

+0

Czy jest jakiś szczególny powód, dla którego nie chcesz korzystać z biblioteki innej firmy? Jak wspomniano powyżej, LinqKit może obsłużyć komponowanie wyrażeń w sposób jaki chcesz; zrobienie tego bez LinqKita prawdopodobnie wymagałoby powielenia wielu jego funkcji. –

Odpowiedz

2

udało mi się rozwiązać problem po 4 godzinach czytania/majstrować przy wyrażeniach (niesamowita technologia).

Włożyłem tej klasy razem (nie jest polerowane lub końcowe, ale pokazuje, w jaki został osiągnięty):

class ParameterRewriter<TTarget , TSource> : ExpressionVisitor { 

    private ParameterExpression Source; 
    private MemberExpression Target; 

    public Expression<Func<TTarget , bool>> Rewrite(Expression<Func<TSource , bool>> predicate , Expression<Func<TTarget , TSource>> propertyNameExpression) { 

     var parameter = Expression.Parameter(typeof(TTarget)); 

     var propertyName = (propertyNameExpression.Body as MemberExpression).Member.Name; 

     Source = predicate.Parameters.Single(); 
     Target = Expression.PropertyOrField(parameter , propertyName); 

     var body = Visit(predicate.Body); 

     return Expression.Lambda<Func<TTarget , bool>>(
      body , 
      parameter 
     ); 

    } 

    protected override Expression VisitParameter(ParameterExpression node) { 

     if (node == Source) { 
      return Target; 
     } 

     return base.VisitParameter(node); 

    } 

} 

i może być stosowany tak:

var parameterRewriter = new ParameterRewriter<Container , Data>(); 
Expression<Func<Data , bool>> dataPredicate = d => (d.Id == 1); 
var containerPredicate = parameterRewriter.Rewrite(dataPredicate , c => c.Data); 

Teoretycznie jednym powinien być w stanie przechodzić głębsze relacje, badając właściwośćNameExpression, ale mam już dość na dzisiaj.

mogę teraz zobaczyć, że SQL Entity Framework jest generowany dla każdego smaku zapytania jest identyczna:

======================= =

Expression<Func<Container , bool>> p = c => c.Data.Id == 1 

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Data_Id] AS [Data_Id] 
FROM [dbo].[Container] AS [Extent1] 
WHERE 1 = [Extent1].[Data_Id] 

========================

Expression<Func<Data , bool>> p = d => d.Id == 1 

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Data_Id] AS [Data_Id] 
FROM [dbo].[Container] AS [Extent1] 
WHERE 1 = [Extent1].[Data_Id] 

=========== =============

+1

@SeanHill Teraz mi powiesz! Haha - tutaj jest 5.30 i mogłem spać;) – FantasticJamieBurns

1

Dołączył klientom filtrowane przez opiera dane, a następnie wybrać klienta z wyniku

public Container Find(Expression<Func<Data, bool>> predicate) 
{ 
    return MyDbContext.Containers 
         .Join(MyDbContext.Containers.Select(c => c.Data) 
                .Where(predicate), 
          c => c.Data.Id, 
          d => d.Id, 
          (c, d) => c) 
         .FirstOrDefault(); 
} 
+0

Nie ma narażonych MyDbContext.Datas. – FantasticJamieBurns

+0

@Jamie 'Data' nie jest jednostką? W każdym razie - możesz dołączyć do stołu samemu. Zaktualizuję odpowiedź –

+0

To nie jest główny agregat. – FantasticJamieBurns

Powiązane problemy