Laden...

LINQ-Abfrage mit mehreren JOINS und doppelten Spaltennamen geht nicht

Erstellt von Bodo08 vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.845 Views
B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
LINQ-Abfrage mit mehreren JOINS und doppelten Spaltennamen geht nicht

verwendetes Datenbanksystem: MSSQL Version 13.0.5270.0
(allerdings liegt mein Problem nicht am Server)

Hi, ich habe folgendes Problem:

Ich versuche jetzt schon ewig in C# eine LINQ-Abfrage mit mehreren JOINS zu erstellen, die mir mehrere Felder aus allen gejoineden Tabellen am Ende in einer Liste rauswirft.

Mein Problem dabei ist, dass in den Unter-Tabellen, teilweise Namensgleichheit besteht, daher würde ich diese gerne während meiner Abfrage umbenennen, v.a. weil diese dann später in ein DataGridView rein kommen.

Ich habe also eine Haupttabelle, die in manchen Feldern eine Zahl stehen hat, welche der FK für eine zweite Tabelle ist (da steht dann der PK).
Sagen wir ich habe in der Maintable die Felder "ma,mb,mc,md,me und mf" drin, welche auf die Tabellen "SA, SC, SD" verweisen und diese Tabellen besitzen (alle drei jeweils) die Felder "ID und Name".
Dann möchte ich gerne nach meiner Abfrage auf die Felder "SAName, MB, SCName, SDName, ME und MF" zugreifen können.
Der einfachheithalber gehen wir von Int-Werten überall aus. 😉

Mein (nicht wie gewünscht funktionierendes) Beispiel wäre dann:


 var Liste =
      (
        from M in Maintable
        join SA in Context.SAs
        on M.ma equals SA.ID

        join SC in Context.SCs
        on M.mc equals SC.ID

        join SD in Context.SDs
        on M.md equals SD.ID

        select new
        {
          SAName = SA.Name,
          MB = M.mb,
          SCName = SC.Name,
          SDName = SD.Name,
          ME = M.me,
          MF = M.mf,
        }).ToList();

Ich habe noch viel zu wenig Ahnung von der Materie (LINQ), das ist mir klar, aber ich kriege hier noch die Krise. Ich suche schon seit Stunden nach einer Lösung und finde irgendwie nichts. 😦

Wäre echt prima, wenn hier jemand eine Lösung hätte.
Falls noch Angaben fehlen, liefere ich die gerne noch nach.
...ich arbeite mit Entity Framework Core

16.834 Beiträge seit 2008
vor 4 Jahren

Was heisst "funktioniert nicht"? Bekommst Du einen Compiler-Fehler, oder was genau ist das Problem? 🤔
Siehe auch [Hinweis] Wie poste ich richtig? Punkt 5 "Problem genau beschreiben - inkl. genauer Fehlermeldung"

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Dachte, das steht alles im Text...
Sorry, falls nicht.

Nunja, der Code, den ich gepostet habe dürte ein Ergebnis liefern (ist nicht getestet, da der Originalcode viel länger und verstrickter ist), nur nicht das gewünschte Ergebnis.
Dieser hier von mir gepostete Code ist allerdings (von der Struktur) genau das, was ich auch schon seit Stunden mehrfach rauf und runter gecoded habe.

Ich hätte gerne, wie im Text beschrieben, die einzelnen "Name"-Felder als Ausgabe (SAName, SCName, SCName), die in meinen Untertabellen (SA, SC, SD) stehen in meiner "Liste"-Liste.

Sprich als Ergebnisliste sollte folgendes Rauskommen:

"SAName, MB, SCName, SDName, ME und MF" bzw. natürlich die Werte der Felder, die ich dann mit den Feldnamen auslesen möchte und in mein DataGridView schreibe.

Ich bekomme ja nach jedem JOIN das "NAME"-Feld überschrieben, der dazugejoineden Tabelle, aber ich brauche statt einem "Name"-Feld letzendlich drei "Name"-Felder (SAName, SCName, SDName).

16.834 Beiträge seit 2008
vor 4 Jahren

Du hast bisher nur erzählt, was Du abstrakt machst - und "geht nicht".
Aber was genau nicht geht, das sagst Du bisher nicht.

Hilfe bei abstrakten Code zu leisten ist in den aller meisten Fällen nicht zielführend; man spricht oft am Problem vorbei, weil man bei der Abstraktion zuviel wegnimmt und der Helfer dann einen völlig anderen Kenntnisstand hat als Du.

Das, was Du im Select machst, ist im Prinzip das Vergeben von Alias-Werten für Spalten.

select new
        {
          SAName = SA.Name,
          MB = M.mb,
          SCName = SC.Name,
          SDName = SD.Name,
          ME = M.me,
          MF = M.mf,
        }).ToList();

SAName ist bereits ein Alias für "SA.Name".
Es kann hier nicht mehr zu doppelten Namen geben - das würde ein Compilerfehler auslösen und ist hier bereits der vorgegriffene Schutz von vermeintlich doppelt verwendeten Bezeichnern.

Mir ist mit Deiner Information alleine immer noch nicht klar, "was bei Dir nicht geht".

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Das mit den Aliasnamen ist mir klar, löst nur mein Problem nicht.
Mein Problem ist, dass ich in den gejoineden Tabellen Namensgleichheit habe.

Ich joine eine Tabelle mit einem Feld namens "Name" und danach joine ich noch eine Tabelle, die das Feld "Name" beinhaltet und danach noch eine Tabelle, die wiederum das Feld "Name" enthält.
Im ersten Fall bezieht sich das "Name"-Feld auf Tabelle a, das zweite auf Tabelle c und das dritte auf Tabelle d.
Ich habe also die Felder a.Name, c.Name und d.Name, die mir aber immer nur als "Name" gejoined werden. Somit bekomme ich am Ende nicht mehr die einzelnen Felder für a.Name, c.Name und d.Name, sondern nur EIN "Name"-Feld, das die Werte der (ich glaube) ersten Tabelle enthält.

Ich brauche aber am Ende DREI "Name"-Spalten, die die Werte von Tabelle a,c und d enthalten, also a.Name, c.Name und d.Name.

Wie stelle ich es nun an, dass ich die Werte aller DREI "Name"-Felder am Ende auslesen kann?

Ich brauche dann einfach sowas wie die Felder mit dem Bezeichnungen SAName, SCName und SDName.

T
2.224 Beiträge seit 2008
vor 4 Jahren

Und genau das löst du doch mit dem Alias.
Somit stehen dann in der Spalte SAName dann auch der Wert aus der Tabelle SA und der Spalte Name.
Du musst am Ende nur aus SAName den Wert lesen um an den Wert aus der Spalte Name der Tabelle SA zu kommen und fertig.

Ansonsten musst du dein Problem mal im Code ausdrücken, wie es aussehen soll.
Deine aktuelle Aussage entspricht nämlich deinem aktuellen Code, weshalb ich hier keinen Fehler oder ein Problem erkennen kann.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Du hast Recht, allerdings habe ich einen gravierenden Fehler in meiner Fragebeschreibung gemacht, also im Code. Erst mal sorry dafür. 😦

Ich brauche einen LEFT OUTER JOIN statt eines INNER JOIN, nochmals sorry, mein Fehler, hatte den abgeänderten Code hierher kopiert 😦

Brauche also auch die Felder, deren Werte null beinhalten, daher der LEFT OUTER JOIN

Mein Code schaut dann so aus:


var Liste =
      (
        from M in Maintable
        join SA in Context.SAs
        on M.ma equals SA.ID into temp1

        from temp2 in temp1.DefaultIfEmpty()
        join SC in Context.SCs
        on M.mc equals SC.ID into temp3

        from temp4 in temp3.DefaultIfEmpty()
        join SD in Context.SDs
        on M.md equals SD.ID into temp5

        from temp6 in temp5.DefaultIfEmpty()
        select new
        {
          SAName = temp2.Name,
          MB = M.mb,
          SCName = temp4.Name,
          SDName = temp6.Name,
          ME = M.me,
          MF = M.mf,
        }).ToList();

Es wird mir zwar in meinem DataGridView alles angezeigt, aber in dem "Name"-Feld stehen dann falsche werte drin <-- DAS ist mein Problem und der Fehler.

Vermutlich liegt es daran, wie ich das ganze joine bzw zusammenbaue, aber ich sehe langsam nur noch "Wald" statt "Bäume" 😦

Und nochmals sorry für den "falschen" Code 😦

Btw.: Soll ich lieber einen neuen Post aufmachen?

16.834 Beiträge seit 2008
vor 4 Jahren

Puh; also mit dem Naming solltest Dich nochmal beschäftigen.
Diese temp-Bezeichner machen es alles andere als übersichtlich.

Ich glaube Du musst im Select statt auf M auf temp1 zugreifen.
Mit M greifst Du IIRC auf Werte zu, die gar nicht dem LOJ entsprechen.

Btw.: Soll ich lieber einen neuen Post aufmachen?

Nein; aber in Zukunft bitte von Anfang an mit Klarheit rausrücken.
Keiner hat Zeit und Lust zum Raten 😉

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

@Abt - das Naming ist klar 😉
- das mit dem Post war ein Versehen, weil ich den falschen Code kopiert hatte, sorry 😕
- auf temp1 geht leider gar nix, der Zugriff auf M im SELECT passt schon (getestet)

Das Problem ist, dass ich die Werte durch das INTO in eine neue Tabelle schreiben lasse und DA bräuchte ich dann die richtigen Namen, die ich wieder weiter reiche.

16.834 Beiträge seit 2008
vor 4 Jahren

DA bräuchte ich dann die richtigen Namen, die ich wieder weiter reiche.

Diese Bezeichner werden von EF automatisch erzeugt; da hast Du keine Einflussname (außer sie haben das geändert, was ich bisher nich gesehen hab - auch nicht in der neuen Preview).
Durch den Zugriff auf die temp. Tabellennamen kann EF das automatisch zuordnen.

Die Namen in Deinem Code muss/soll ja je nach Mapping nicht 1:1 den echten Tabellennamen entsprechen.

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Heißt, Es geht nicht?

Oder wie bekomme ich die Felder dann weiter gereicht, so dass ich am Ende dann meine Felder mit richtigem Namen und richtigen Werten habe?

16.834 Beiträge seit 2008
vor 4 Jahren

Oder wie bekomme ich die Felder dann weiter gereicht, so dass ich am Ende dann meine Felder mit richtigem Namen und richtigen Werten habe?

EF ist eine Abstraktion Deiner Datenbankkommunikation.
Wenn Du Dir den generierten SQL Code anschaust (siehe SQL Profiler oder EF Core Logging), dann wirst Du da viel kryptische Bezeichner finden, weil EF ein eigenes Mapping im Hintergrund hat, um Eindeutigkeiten sicherzustellen.

Daten werden korrekt von A nach B transportiert, indem Du die Schlüssel-Bezeichner im LINQ-Code verwendest.

Aktivier mal das Logging im EF Core und schau Dir an, ob der SQL Code so generiert wird, wie Du es denkst.
Dann kannst ja anylisieren, ob es an EF Core oder an Deinen Queries liegt und ggfls. die SQL Befehle direkt via Management Studio gegen die DB jagen, um zu vergleichen.

Logging aktiviert man via LoggerFactory:

            
        public static readonly LoggerFactory DebugLoggerFactory
            = new LoggerFactory(new[] { new DebugLoggerProvider() }); // NuGet: Microsoft.Extensions.Logging.Debug

           // DI
            services.AddDbContext<MyCSharpEFCoreDbContext>(o =>
            {
                o.UseSqlServer(dbOptions.ConnectionString);
                o.UseLoggerFactory(DebugLoggerFactory);

            });
B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Danke @Abt, dass du dich meines Problems annimmst, aber der Code von dir funktioniert nicht oder ich weiß nicht wo der hin soll (hab Mehreres versucht).

Mir würde eigentlich auch schon reichen, falls jemand eine Lösung hat, dass er mir den Code evtl hier posten kann. Ich verstehe das Meiste durch ausprobieren. Die Hintergründe sind mir zwar nicht komplett unbekannt, aber so tief wollte ich da jetzt nicht rein.
Ich könnte auch einfach die Feldnamen umbenennen, dann habe ich das Problem gar nimmer, aber das löst das Problem ja nicht und vielleicht kann das hier ja nochmal irgendwer brauchen. 😉

16.834 Beiträge seit 2008
vor 4 Jahren

Der Code funktioniert - ist produktiv in mehreren meiner Projekte - darunter der Quellcode von einem Teil des MyCSharp-Forums - im Einsatz.
Wie man sieht ist das nichts anderes als die Dependency Injection Registrierung von EF Core.

Wie verwendest Du denn den DBContext und Co?
Ohne DI?

Ich verstehe das Meiste durch ausprobieren. Die Hintergründe sind mir zwar nicht komplett unbekannt, aber so tief wollte ich da jetzt nicht rein.

Dann führt dann halt zu Verständnisprobleme wie man hier sieht; Dir ist zB offenbar die Abstraktion völlig unbekannt, was hier nun zum Anwendungsproblem wird.

Leider verstehe ich immer noch nicht 100% Dein Vorhaben; bezweifle aber, dass EF Core dies nicht bereits abdeckt.

B
Bodo08 Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Also mal lieben Dank an alle...

Ich hab meinen Fehler gefunden, es lag nicht am Code es war eindeutig ein "Schicht 8"-Problem.
Ich sollte mal meine Augen kontrollieren lassen...

Habe im Code einen Begriff falsch geschrieben (Buchstabe falsch) das zufälligerweise im join zu einem anderen match geführt hat, daher haben die Ergebnisse nicht gestimmt.

Die Abfrage funktioniert scheinbar korrekt.

Sorry nochmal für die Missverständnisse.

Wo schreibe ich jetzt das [Closed] hin?