Hallo!
Ich habe eine Linq-Relation die mir den ersten Darsteller aus einer Darsteller-Liste zurückgeben soll:
join Dar in DarstellerListe on DVD.DarstellerEinträge.FirstOrDefault().DarstellerID equals Dar.ID
Zum Verständnis: DVD ist der "Datensatz" der die Eigenschaft DarstellerEinträge hat, die eine Liste der Darsteller darstellt. Dessen 1. Eintrag ich über die Relation der DarstellerID zu einer Darsteller-Liste setzen möchte.
Das funktioniert so lang, wie die Darsteller-Liste auch Einträge hat. (Mir wird korrekt der 1. Darsteller zurückgegeben)
Wenn die Liste keine Einträge hat, wird durch FirstOrDefault() der Wert null (Defaultwert eines Objektes) und null hat natürlich keine Eigenschaft DarstellerID. Es wird eine Exception geworfen.
Ich habe im Internet noch einen Ansatz gefunden, der darauf basiert, dass FirstOrDefault() dadurch ersetzt wird, dass man die Darsteller-Liste sortiert (OrderBy), danach sich den ersten Eintrag zurückgeben lässt (Take(1)) und wenn dies nicht möglich ist (DefaultIfEmpty) als Ersatz-Wert einen neuen Darsteller-Eintrag erzeugt:
join Dar in DarstellerListe on DVD.DarstellerEinträge.OrderBy(x => x.DarstellerPosition).Take(1).DefaultIfEmpty(new DarstellerEintrag()) equals Dar
Klingt plausibel! (Bei leerer Liste:) Ein neuer Darsteller-Eintrag stimmt mit keinem Eintrag in der Darsteller-Liste überein die Relation ist nicht "vorhanden". Aber es wird auch keine Exception geworfen.
Der join scheint aber den gesamten Darsteller-Eintrag (1. oder Neuen) mit den Darsteller-Eintrag aus der Darsteller-Liste nicht vergleichen zu können. Fehlermeldung:
CS1941 Der Typ eines Ausdrucks in der join-Klausel ist falsch. Fehler beim Typrückschluss im Aufruf von "GroupJoin".
Ich habe aber keinen Weg gefunden die ID (wie im obigen join) anzugeben.
Zeig Dein gesamten Query, sodass wir den Kontext kennen. So müssen wir raten, ob die Exception wirklich davon kommt oder einfach nur ein Folgefehler ist.
Ebenso gib bitte Quellen an, wenn Du von Dingen sprichst wie
Ich habe im Internet noch einen Ansatz gefunden, der darauf basiert,
Weird ist, dass dort offenbar von Take(1) gesprochen wird, wo doch FirstOrDefault() der effizientere Weg ist. Für uns nicht ersichtlich, ob der Kontext hier stimmt.
Wenn die Liste keine Einträge hat, wird durch FirstOrDefault() der Wert null (Defaultwert eines Objektes) und null hat natürlich keine Eigenschaft DarstellerID. Es wird eine Exception geworfen.
Aus linq-sicht würde man eigentlich den join Wegoptimieren, wenn die Ausgangssituation null ist aka "conditional join".
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt!
Die Quelle müsste ich erst wieder suchen. Habe schon einiges recherchiert bevor ich hier frage ...
Den join um den es geht habe ich hier abgesetzt dargestellt:
DVDEinträgeSort = // DVD-Einträge Sortierungs-Liste aktualisieren
(from DVD in DVDEinträge // Left Data Source
join Reg in RegisseurListe on DVD.Regisseur equals Reg.ID // Inner Join Regisseure
into Gruppe
from Reg2 in Gruppe.DefaultIfEmpty() // Performing Left Outer Join
join Gen in GenreListe on DVD.Genre.FirstOrDefault() equals Gen.ID // Inner Join Genre (1. Wert)
into Gruppe2
from Gen2 in Gruppe2.DefaultIfEmpty() // Performing Left Outer Join
join Land in LänderListe on DVD.Land.FirstOrDefault() equals Land.ID // Inner Join Land (1. Wert)
into Gruppe3
from Land2 in Gruppe3.DefaultIfEmpty() // Performing Left Outer Join
join Dar in DarstellerListe on DVD.DarstellerEinträge.FirstOrDefault().DarstellerID equals Dar.ID // Inner Join Darsteller (1. Wert)
into Gruppe4 from Dar2 in Gruppe4.DefaultIfEmpty() // Performing Left Outer Join
select new DVDEintragSort(DVD,
Reg2 != null ? Reg2.Name : string.Empty,
Gen2 != null ? Gen2.Bezeichnung : string.Empty,
Land2 != null ? Land2.Bezeichnung : string.Empty,
Dar2 != null ? Dar2.Name : string.Empty)
).ToList();
Bei dieser Variante wird eine NullReferenceException geworfen: (siehe Bild) Der Debugger zeigt auch genau die Stelle.
System.NullReferenceException
HResult=0x80004003
Nachricht = Object reference not set to an instance of an object.
Danke! für deine unermüdliche Unterstützung!
In meinen Augen macht das kein Sinn und kann so kaum funktionieren.
Du willst ja nix anderes als ein Genre anhand einer Id.
Mach doch einfach Sub-Queries, die null sein dürfen, statt Joins. Du kannst so die Nulls nicht anfangen.
Hier mal exemplarisch das Land-Join aufgelöst
DVDEinträgeSort = // DVD-Einträge Sortierungs-Liste aktualisieren
(from DVD in DVDEinträge // Left Data Source
// Land als Subquery
let dvdLand = DVD.Land.FirstOrDefault()
let landEntity = dvdLand == null ? null : LänderListe.Where(e=>e.Id == dvdLand.Id).SingleOrDefault() // keine Ahnung ob das mit der Id so stimmt
join Reg in RegisseurListe on DVD.Regisseur equals Reg.ID // Inner Join Regisseure
into Gruppe
from Reg2 in Gruppe.DefaultIfEmpty() // Performing Left Outer Join
join Gen in GenreListe on DVD.Genre.FirstOrDefault() equals Gen.ID // Inner Join Genre (1. Wert)
into Gruppe2
from Gen2 in Gruppe2.DefaultIfEmpty() // Performing Left Outer Join
join Land in LänderListe on DVD.Land.FirstOrDefault() equals Land.ID // Inner Join Land (1. Wert)
into Gruppe3
from Land2 in Gruppe3.DefaultIfEmpty() // Performing Left Outer Join
join Dar in DarstellerListe on DVD.DarstellerEinträge.FirstOrDefault().DarstellerID equals Dar.ID // Inner Join Darsteller (1. Wert)
into Gruppe4 from Dar2 in Gruppe4.DefaultIfEmpty() // Performing Left Outer Join
select new DVDEintragSort(DVD,
Reg2 != null ? Reg2.Name : string.Empty,
Gen2 != null ? Gen2.Bezeichnung : string.Empty,
// Land Entity
landEntity!= null ? landEntity.Bezeichnung : string.Empty,
Dar2 != null ? Dar2.Name : string.Empty)
).ToList();
PS: es ist super ineffizient strings, die "nichts" darstellen, mit "" zu deklarieren. Dafür gibts null.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt!
Was ich alles ausprobiert habe! (Ich hatte immer das Problem der zusätzlichen Ebene bei den Darsteller-Einträgen)
Und du umgehst das Problem so einfach!
Jetzt wo ich meinen LINQ Left Outer Join verstanden habe machst du alles wieder zunichte! 😉
Das Prinzip hat mich aber schon überzeugt! So kann ich in jeder Ebene prüfen, ob ein (Listen) Wert auch vorhanden ist!
Struktur:
Liste DVD-Einträge
° DVD-Eintrag1
° Liste Land (Eigenschaft Land)
° Land1 ID → Land
° Land2 ID → Land
° Liste Genre (Eigenschaft Genre)
° Genre1 ID → Genre
° Genre2 ID → Genre
° Liste Darsteller-Einträge (Eigenschaft DarstellerEinträge)
° Darsteller-Eintrag1
° Darsteller-Position
° Darsteller ID → Darsteller
° Darsteller-Eintrag2
° Darsteller-Position
° Darsteller ID → Darsteller
Zugegebener maßen habe ich den Eindruck, dass diese Lösung etwas langsamer ist (Darsteller sind es zur Zeit ca. 78.000 die jedes mal von den 8.000 DVD-Einträgen "gefiltert" werden), aber 10s beim Start sind noch ok.
Bleibt mir nur noch wieder vielen Dank!!!! zu sagen!
PS: Kann man hier auch Leerzeichen erzeugen die bleiben?
Zitat von perlfred
Zugegebener maßen habe ich den Eindruck, dass diese Lösung etwas langsamer ist (Darsteller sind es zur Zeit ca. 78.000 die jedes mal von den 8.000 DVD-Einträgen "gefiltert" werden), aber 10s beim Start sind noch ok.
Gerade Sub-Selects kann man durchaus optimieren. Aber im Endeffekt kann man mit EF Core ADO.NET dazu bringen, dass parallele Queries mittels eines Requests ausgeführt werden. Zudem profitiert ein solcher Query immens von korrekten DB Indizes.
Ein Geschwindigkeitsvergleich ist nur dann valide, wenn beide Queries auch das gleiche Resultat bringen.
Musst halt schauen was auf dem SQL Server ausgegeben wird und ob das generierte SQL eben suboptimal, oder obs an der Query-Optimierung liegt.
Schau Dir dazu den Ausführungsplan an, was langsam ist.
Verwende diese Art von Queries in Dutzenden von Projekten, auch das Forum hier.
Da sind Datenmengen mit PT dabei - und das funktioniert einwandfrei und super schnell.
PS: Kann man hier auch Leerzeichen erzeugen die bleiben?
Auch Leerzeichen sind unnötige Speicherallokierung. Wozu Dinge allokieren und Rechenpower nutzen, wenn mans nicht braucht.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code