Wie können Sie anhand des folgenden einfachen Beispiels die Ergebnisse mehrerer Tabellen mit Linq to SQL am besten zurückgeben?
Angenommen, ich habe zwei Tabellen:
Dogs: Name, Age, BreedId
Breeds: BreedId, BreedName
Ich möchte alle Hunde mit ihren zurückgeben BreedName
. Ich sollte alle Hunde dazu bringen, so etwas ohne Probleme zu benutzen:
public IQueryable<Dog> GetDogs()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select d;
return result;
}
Aber wenn ich Hunde mit Rassen will und dies versuche, habe ich Probleme:
public IQueryable<Dog> GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
Name = d.Name,
BreedName = b.BreedName
};
return result;
}
Jetzt ist mir klar, dass der Compiler es mir nicht erlaubt, eine Reihe anonymer Typen zurückzugeben, da er Hunde erwartet. Gibt es jedoch eine Möglichkeit, dies zurückzugeben, ohne einen benutzerdefinierten Typ erstellen zu müssen? Oder muss ich eine eigene Klasse für DogsWithBreedNames
diesen Typ erstellen und diesen in der Auswahl angeben? Oder gibt es einen anderen einfacheren Weg?
c#
linq
linq-to-sql
Jonathan S.
quelle
quelle
foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);
Antworten:
Ich tendiere dazu, dieses Muster zu wählen:
Es bedeutet, dass Sie eine zusätzliche Klasse haben, aber es ist schnell und einfach zu codieren, leicht erweiterbar, wiederverwendbar und typsicher.
quelle
Sie können anonyme Typen zurückgeben, aber es ist wirklich nicht schön .
In diesem Fall wäre es meiner Meinung nach weitaus besser, den entsprechenden Typ zu erstellen. Wenn es nur innerhalb des Typs verwendet werden soll, der die Methode enthält, machen Sie es zu einem verschachtelten Typ.
Persönlich möchte ich, dass C # "benannte anonyme Typen" erhält - dh dasselbe Verhalten wie anonyme Typen, jedoch mit Namen und Eigenschaftsdeklarationen, aber das war's.
BEARBEITEN: Andere schlagen vor, Hunde zurückzugeben und dann über einen Eigenschaftspfad usw. auf den Rassennamen zuzugreifen. Dies ist ein durchaus vernünftiger Ansatz, aber IME führt zu Situationen, in denen Sie aufgrund der gewünschten Daten eine bestimmte Abfrage durchgeführt haben Verwenden Sie - und diese Metainformationen gehen verloren, wenn Sie gerade zurückkehren
IEnumerable<Dog>
- die Abfrage erwartet möglicherweise, dass Sie (sagen wir)Breed
anstelleOwner
einiger Ladeoptionen usw. verwenden. Wenn Sie dies jedoch vergessen und andere Eigenschaften verwenden, funktioniert Ihre App möglicherweise jedoch nicht so effizient, wie Sie es sich ursprünglich vorgestellt hatten. Natürlich könnte ich Müll reden oder überoptimieren, etc ...quelle
Nur um meinen Wert von zwei Cent zu addieren :-) Ich habe kürzlich gelernt, wie man mit anonymen Objekten umgeht. Es kann nur verwendet werden, wenn auf das .NET 4-Framework abgezielt wird, und zwar nur, wenn ein Verweis auf System.Web.dll hinzugefügt wird, aber dann ist es ganz einfach:
Um einen Verweis auf System.Web.dll hinzufügen zu können, müssen Sie den Ratschlägen von Rushonerok folgen: Stellen Sie sicher , dass das Zielframework Ihres [Projekts] ".NET Framework 4" und nicht ".NET Framework 4-Clientprofil" ist.
quelle
Nein, Sie können keine anonymen Typen zurückgeben, ohne einige Tricks durchzugehen.
Wenn Sie C # nicht verwenden, wird das, wonach Sie suchen (Rückgabe mehrerer Daten ohne konkreten Typ), als Tupel bezeichnet.
Es gibt viele C # -Tupel-Implementierungen. Wenn Sie die hier gezeigte verwenden , würde Ihr Code so funktionieren.
Und auf der anrufenden Seite:
quelle
... select Tuple.Create(d, b)
.Sie könnten so etwas tun:
quelle
Sie müssen zuerst die
ToList()
Methode verwenden, um Zeilen aus der Datenbank zu entnehmen und dann Elemente als Klasse auszuwählen. Versuche dies:Der Trick ist also der erste
ToList()
. Es wird sofort die Abfrage durchgeführt und die Daten aus der Datenbank abgerufen. Der zweite Trick besteht darin, Elemente auszuwählen und mithilfe des Objektinitialisierers neue Objekte mit geladenen Elementen zu generieren.Hoffe das hilft.
quelle
In C # 7 können Sie jetzt Tupel verwenden! ... wodurch die Notwendigkeit entfällt, eine Klasse zu erstellen, um das Ergebnis zurückzugeben.
Hier ist ein Beispielcode:
Möglicherweise müssen Sie jedoch das System.ValueTuple-Nuget-Paket installieren.
quelle
Jetzt ist mir klar, dass der Compiler es mir nicht erlaubt, eine Reihe anonymer Typen zurückzugeben, da er Hunde erwartet. Gibt es jedoch eine Möglichkeit, dies zurückzugeben, ohne einen benutzerdefinierten Typ erstellen zu müssen?
Verwenden Sie use object , um eine Liste anonymer Typen zurückzugeben, ohne einen benutzerdefinierten Typ zu erstellen. Dies funktioniert ohne den Compilerfehler (in .net 4.0). Ich habe die Liste an den Client zurückgegeben und sie dann in JavaScript analysiert:
quelle
Wählen Sie einfach Hunde aus und verwenden Sie sie
dog.Breed.BreedName
. Dies sollte gut funktionieren.Wenn Sie viele Hunde haben, verwenden Sie DataLoadOptions.LoadWith, um die Anzahl der Datenbankaufrufe zu verringern.
quelle
Sie können anonyme Typen nicht direkt zurückgeben, sondern sie durch Ihre generische Methode schleifen. Dies gilt auch für die meisten LINQ-Erweiterungsmethoden. Es gibt dort keine Magie, obwohl es so aussieht, als würden sie anonyme Typen zurückgeben. Wenn der Parameter anonym ist, kann das Ergebnis auch anonym sein.
Unten ein Beispiel basierend auf Code aus der ursprünglichen Frage:
quelle
Wenn Sie Hunde zurückgeben, tun Sie Folgendes:
Wenn Sie möchten, dass die Rasse eifrig und nicht faul geladen wird, verwenden Sie einfach das entsprechende DataLoadOptions- Konstrukt.
quelle
BreedId
in derDog
Tabelle ist offensichtlich ein Fremdschlüssel für die entsprechende Zeile in derBreed
Tabelle. Wenn Sie Ihre Datenbank ordnungsgemäß eingerichtet haben, sollte LINQ to SQL automatisch eine Zuordnung zwischen den beiden Tabellen erstellen. Die resultierende Hundeklasse hat eine Breed-Eigenschaft, und die Breed-Klasse sollte eine Dogs-Sammlung haben. Wenn Sie es auf diese Weise einrichten, können Sie trotzdem zurückkehren. DiesIEnumerable<Dog>
ist ein Objekt, das die Rasseeigenschaft enthält. Die einzige Einschränkung besteht darin, dass Sie das Rassenobjekt zusammen mit den Hundeobjekten in der Abfrage vorab laden müssen, damit auf sie zugegriffen werden kann, nachdem der Datenkontext entsorgt wurde, und (wie in einem anderen Poster vorgeschlagen) eine Methode für die Sammlung ausführen müssen, die das verursacht Abfrage muss sofort ausgeführt werden (in diesem Fall ToArray):Es ist dann trivial, für jeden Hund auf die Rasse zuzugreifen:
quelle
Wenn die Hauptidee darin besteht, dass die an den Datenbankserver gesendete SQL-Select-Anweisung nur die erforderlichen Felder und nicht alle Entitätsfelder enthält, können Sie Folgendes tun:
quelle
Versuchen Sie dies, um dynamische Daten zu erhalten. Sie können Code für List <> konvertieren
quelle
Wenn Sie in Ihrer Datenbank ein Beziehungssetup mit einer falschen Schlüsselbeschränkung für BreedId haben, bekommen Sie das nicht schon?
So kann ich jetzt anrufen:
Und in dem Code, der das nennt:
In Ihrem Fall würden Sie also so etwas wie dog.Breed.BreedName aufrufen - wie gesagt, dies hängt davon ab, dass Ihre Datenbank mit diesen Beziehungen eingerichtet ist.
Wie bereits erwähnt, helfen die DataLoadOptions dabei, die Datenbankaufrufe zu reduzieren, wenn dies ein Problem darstellt.
quelle
Dies beantwortet Ihre Frage nicht genau, aber Google hat mich anhand der Keywords hierher geführt. So können Sie einen anonymen Typ aus einer Liste abfragen:
quelle