Problem mit Include und EF Core 3.1.1

  • WPF

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    Problem mit Include und EF Core 3.1.1

    Hallo Leute,

    ich habe letzte Woche ein Update auf EF Core 3.1.1 durchgeführt.
    Es haben sich ja einige Dinge gewaltig geändert, zum Beispiel wird keine implizierten Client Transactionen für LINQ Queries mehr unterstützt.

    Das bisher verwendete GenericRepository (von Sascha) zickt daher rum, ich musste die GetAll Methode z.b. umschreiben:

    VB.NET-Quellcode

    1. query = query.AsEnumerable.Where(Function(x) DirectCast(x, ILogicalDelete).DeletedFlag = False).AsQueryable


    Leider ist mir jetzt aufgefallen das auch die .Include Methode nicht mehr funktionert. Ich habe schon einiges probiert und rumgesucht aber ich verstehe nicht warum das hier nicht funktioniert:

    VB.NET-Quellcode

    1. Dim query As IQueryable(Of CombustionChamber) = _Rep.GetAll(vWithTracking, vIncludeDeleted)
    2. query = query.Include("CombustionChamberWallLayers")
    3. Return query.ToList


    Die Extension Methode:

    VB.NET-Quellcode

    1. <Extension>
    2. Public Function Include(Of TEntity As Class)(ByVal queryable As IQueryable(Of TEntity), prop As String) As IQueryable(Of TEntity)
    3. Return EntityFrameworkQueryableExtensions.Include(Of TEntity)(queryable, navigationPropertyPath:=prop)
    4. End Function


    Es wird kein Error ausgeworfen rein gar nichts, der Effekt ist das die Navigation Property einfach leer bleibt und nicht geladen wird.
    Wenn ich Lazy Loading aktiviere funktioniert die Navigation Property, allerdings will ich LazyLoading aus Performancetechnischer Hinsicht eigentlich vermeiden.

    Hat jemand einen Tipp für mich?

    -----------
    Edit:
    Hab jetzt zumindest eine Lösung gefunden, wenn man zuerst den Include macht und dann erst eine "Where" draufsetzt funktioniert es.
    Unschön an der Lösung das man, wie in meinem Fall wenn man mit LogicalDelete arbeitet immer eine zusätzliche Zeile benötigt:

    VB.NET-Quellcode

    1. Public Function GetAll(vWithTracking As Boolean, vIncludeDeleted As Boolean, vIncludeNavProperties As Boolean) As IEnumerable(Of T)
    2. Dim query As IQueryable(Of CombustionChamber) = _Rep.GetAll(vWithTracking)
    3. if vIncludeNavProperties then query = query.Include("CombustionChamberWallLayers")
    4. query = _Rep.RemoveDeleted(query, vIncludeDeleted)
    5. Return query.ToList
    6. End Function


    und meine "Remove Deleted" im GenericRepository:

    VB.NET-Quellcode

    1. Public Overridable Function RemoveDeleted(query As IQueryable(Of T), vIncludeDeleted As Boolean) As IQueryable(Of T)
    2. If vIncludeDeleted = False AndAlso GetType(ILogicalDelete).IsAssignableFrom(GetType(T)) Then
    3. query = query.AsEnumerable.Where(Function(x) DirectCast(x, ILogicalDelete).DeletedFlag = False).AsQueryable
    4. End If
    5. Return query
    6. End Function


    Das muss man doch irgendwie schöner hinkriegen oder?
    mfG.
    Stephan

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kaifreeman“ ()

    Hallo Stephan

    kaifreeman schrieb:

    zum Beispiel wird keine implizierten Client Transactionen für LINQ Queries mehr unterstützt

    Zum Glück, das war für Anfänger immer eine große Stolperfalle.

    kaifreeman schrieb:

    Leider ist mir jetzt aufgefallen das auch die .Include Methode nicht mehr funktionert

    ??? Das muss ich mal probieren....

    kaifreeman schrieb:

    Hab jetzt zumindest eine Lösung gefunden, wenn man zuerst den Include macht und dann erst eine "Where" draufsetzt funktioniert es.

    Im Grunde müsste es egal sein wo es ist. Ich werde (weil es mich interessiert) mal testen wie sich das mit der neuen Version verhält.

    Bevor ich nun selbst Klassen, ein Repository usw. erstelle, hast du ein Demobeispiel zur Hand? ansonsten werde ich schnell mal was bauen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi,
    also bei mir funktioniert das in 3.1.1 nach wie vor.

    C#-Quellcode

    1. public Person Get(int id)
    2. {
    3. return Context.People.Where(p => p.Id == id).Include(l => l.AcessData).FirstOrDefault();
    4. }


    Ich nutze aber keine eigene Include sondern die aus Microsoft.EntityFrameworkCore.Query.
    "Hier könnte Ihre Werbung stehen..."
    @Nofear23m
    Ich glaube das Thema ist einfach das ich durch die impliziete Client Transaction, die ich ja umbauen musste, zuvor mit .asenumerable die Abfrage im Client "materialisiere" und danach EF Core einfach nicht keine Chance hat den Include zu erwirken.
    Bzgl. Demo kann ich dir nur anbieten mein Projekt auszuräumen und hochzuladen.

    @MichaHo
    Im Datacontext funktioniert das ja auch, aber anscheinend nicht wenn man innerhalb der "Where" Clause nach einem Interface casten möchte (was ja auch logisch ist da ja der SQL Server das nicht verarbeiten kann.

    Ich werde mal tüfteln ob mir eine schönere Lösung einfällt.
    Danke euch inzwischen.
    mfG.
    Stephan
    Ja, lade das mal bitte hoch. Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    @Nofear23m

    Projekt im Anhang, ich habe es jetzt nicht im Großen Stil bereinigt.
    -removed-

    Ps: Nicht wundern, ich bin da mitten in der Arbeit und sehr vieles ist bei weitem nicht fertig, und vieles wirst du sicher wiedererkennen :).
    mfG.
    Stephan

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kaifreeman“ ()

    Hallo

    Da das Projekt leider nicht lauffähig ist (88 Fehler) muss ich etwas raten.

    Aber was mir nun aufgefallen ist (hätte mir in deinem Post #1 bereits auffallen sollen) ist das AsEnumerable() im Query.
    Das führt dazu das in diesem Moment Materialisiert wird, also eine Abfrage an die DB geht. Kommt das Include danach kann es nichts mehr bewirken, es wurde ja bereits abgerufen.
    Das dies vorher funktioniert hat glaube ich aber irgendwie nicht.

    Weiters solltest du versuchen Includes nicht als String anzugeben sondern als Expressions, so können Tippfehler vermieden werden.

    Probier doch bitte mal folgendes:

    VB.NET-Quellcode

    1. Public Function GetAll(vWithTracking As Boolean, vIncludeDeleted As Boolean, vIncludeNavProperties As Boolean) As IEnumerable(Of CombustionChamber)
    2. Dim query As IQueryable(Of CombustionChamber) = _Rep.GetAll(vWithTracking)
    3. If Not vIncludeDeleted Then query = query.Where(Function(x) x.DeletedFlag = False)
    4. If vIncludeNavProperties Then query = query.Include(Function(x) x.CombustionChamberWallLayers)
    5. Return query.ToList
    6. End Function


    Ich habe den Code jetzt nicht durchgesehen, aber folgendes ist mir ind Auge gesprungen da es ja um diese Methode geht:

    VB.NET-Quellcode

    1. Public Overridable Function GetAll(Optional tracking As Boolean = False) As IQueryable(Of T) Implements IGenericRepository(Of T).GetAll
    2. Debug.WriteLine($"Calling GetAll. Tracking: {tracking.ToString}")
    3. Dim query As IQueryable(Of T) = CType(_entities, MyMassBalancerContext).[Set](Of T)()
    4. If tracking Then query = query.AsTracking
    5. Debug.WriteLine("GetAll - Return Query - Amount" & query.Count.ToString)
    6. Return query
    7. End Function


    Klar, du willst loggen wieviele Datensätze betroffen sind, was aber erstens nichts bringt da die Methode ein IQueryable zurückgibt - die Abfrage also ja noch verändert werden kann und somit eine völlig andere Anzahl im Endeffekt zurückgegeben wird, und zum anderen wird damit IMMER ein Roundtrip zur DB ausgeführt. Nicht so dolle, vorallem wenn das Ergebnis dann sowieso für die Fische ist.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Sascha,

    danke für deine Unterstützung. Deine GetAll funktioniert bestens. Somit ist klar das die ursprüngliche Variante die Abfrage nach dem "Deleted" auf Basis des Interfaces nicht mehr im GenericRepository erfolgen, sondern in der BL oder einem typisiertem Repository erfolgen muss.
    Die Debugs im GetAll hab ich rausgenommen, keine Ahnung wie die da gelandet sind X/
    mfG.
    Stephan

    kaifreeman schrieb:

    Somit ist klar das die ursprüngliche Variante die Abfrage nach dem "Deleted" auf Basis des Interfaces nicht mehr im GenericRepository erfolgen, sondern in der BL oder einem typisiertem Repository erfolgen muss.

    Verstehe ich jetzt nicht. Kann bleiben wo es ist. Ich habs ja auch drinnen wie du oben siehst. Das .AsEnumerable muss nur weg da in diesem Moment ja Materialisiert wird! und alles danach dann im RAM (also am Client) gefiltert wird.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##