209 Stimmen

Ergebnisse vom anonymen Typ zurückgeben?

Was ist die beste Methode, um mit Linq to SQL Ergebnisse aus mehreren Tabellen zurückzugeben, und zwar anhand des folgenden einfachen Beispiels?

Angenommen, ich habe zwei Tabellen:

Dogs:   Name, Age, BreedId
Breeds: BreedId, BreedName

Ich möchte alle Hunde mit ihren BreedName . Ich sollte alle Hunde mit so etwas ohne Probleme bekommen:

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 das 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 erkenne ich, dass der Compiler nicht lassen Sie mich einen Satz von anonymen Typen zurückgeben, da es Dogs erwartet, aber gibt es eine Möglichkeit, dies zurückzugeben, ohne einen benutzerdefinierten Typ zu erstellen? Oder muss ich meine eigene Klasse für DogsWithBreedNames und geben Sie diesen Typ in der Select? Oder gibt es einen anderen, einfacheren Weg?

0 Stimmen

Nur so aus Neugier, warum zeigen alle Linq-Beispiele mit anonymen Typen, wenn sie nicht funktionieren. Z.B., dieses Beispiel tut foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);

0 Stimmen

@Hot Licks - die Tabelle Customer in diesen Beispielen ist eine Entität, die durch eine Klasse dargestellt wird. Das Beispiel scheint nur nicht die Definitionen dieser Klassen zu zeigen.

0 Stimmen

Sie erfahren auch nicht, dass ein Compilerfehler "var" durch den Klassennamen ersetzt.

226voto

teedyay Punkte 22763

Ich neige dazu, dieses Muster zu wählen:

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public IQueryable<DogWithBreed> 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 DogWithBreed()
                        {
                            Dog = d,
                            BreedName = b.BreedName
                        };
    return result;
}

Das bedeutet, dass Sie eine zusätzliche Klasse haben, aber sie ist schnell und einfach zu programmieren, leicht erweiterbar, wiederverwendbar und typsicher.

0 Stimmen

Dieser Ansatz gefällt mir, aber jetzt bin ich nicht sicher, wie ich den Namen des Hundes anzeigen soll. Wenn ich das Ergebnis an ein DataGrid binde, kann ich die Eigenschaften von Dog abrufen, ohne sie explizit in der Klasse DogWithBreed zu definieren, oder muss ich die Getter/Setter für jedes Feld erstellen, das ich anzeigen möchte?

4 Stimmen

Ist es in DataGrids nicht möglich, die Eigenschaft "Dog.Name" anzugeben? Ich weiß nicht mehr, warum ich sie so sehr hasse, dass ich sie nie verwende...

0 Stimmen

@JonathanS. wie u tat dies in Vorlage Spalte? bitte sagen Sie mir, ich bin in ähnlicher Situation

71voto

Jon Skeet Punkte 1325502

Sie kann anonyme Typen zurückgeben, aber es ist wirklich nicht schön .

In diesem Fall wäre es meiner Meinung nach viel besser, den entsprechenden Typ zu erstellen. Wenn es nur innerhalb des Typs, der die Methode enthält, verwendet werden soll, machen Sie es zu einem verschachtelten Typ.

Ich persönlich würde es begrüßen, wenn C# "benannte anonyme Typen" bekäme - d.h. das gleiche Verhalten wie bei anonymen Typen, aber mit Namen und Eigenschaftsdeklarationen, aber das war's.

EDIT: Andere schlagen vor, Hunde zurückzugeben und dann über einen Eigenschaftspfad usw. auf den Rassenamen zuzugreifen. Das ist ein durchaus vernünftiger Ansatz, aber IME führt es zu Situationen, in denen Sie eine Abfrage in einer bestimmten Weise wegen der Daten, die Sie verwenden möchten getan haben - und dass Meta-Informationen verloren gehen, wenn Sie nur zurückgeben IEnumerable<Dog> - kann die Abfrage lauten in Erwartung von Sie verwenden (sagen wir) Breed よりも Owner aber wenn man das vergisst und andere Eigenschaften verwendet, kann es sein, dass die Anwendung zwar funktioniert, aber nicht so effizient ist, wie man es sich ursprünglich vorgestellt hat. Natürlich könnte ich Unsinn reden, oder über-optimiert sein, etc...

4 Stimmen

Hey, ich bin nicht jemand, der Features nicht will, weil er Angst hat, dass sie missbraucht werden, aber können Sie sich vorstellen, was für ein miserabler Code wir sehen würden, wenn sie es erlauben würden, dass benannte anonyme Typen weitergegeben werden? (Schauder)

22 Stimmen

Es könnte zu einem Missbrauch kommen. Wir könnten auch einen viel einfacheren Code sehen, bei dem wir im Grunde nur ein Tupel wollen. Nicht alles muss ein Objekt mit komplexem Verhalten sein. Manchmal sind "nur die Daten" die richtige Sache. IMO, versteht sich.

1 Stimmen

Danke, Sie bevorzugen es also, Typen zu erstellen, auch wenn es sich um eine einmalige Ansicht wie diese handelt? Ich habe viele Berichte, die dieselben Daten auf unterschiedliche Weise aufbereiten, und ich hatte gehofft, nicht alle diese verschiedenen Typen (DogsWithBreeds, DogsWithOwnerNames usw.) erstellen zu müssen.

18voto

Peter Perháč Punkte 20034

Ich möchte nur meinen Senf dazugeben :-) Ich habe vor kurzem eine Methode für den Umgang mit anonymen Objekten gelernt. Sie kann nur verwendet werden, wenn man das .NET 4-Framework anvisiert und das auch nur, wenn man einen Verweis auf System.Web.dll hinzufügt, aber dann ist es ganz einfach:

...
using System.Web.Routing;
...

class Program
{
    static void Main(string[] args)
    {

        object anonymous = CallMethodThatReturnsObjectOfAnonymousType();
        //WHAT DO I DO WITH THIS?
        //I know! I'll use a RouteValueDictionary from System.Web.dll
        RouteValueDictionary rvd = new RouteValueDictionary(anonymous);
        Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]);
    }

    private static object CallMethodThatReturnsObjectOfAnonymousType()
    {
        return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" };
    }
}

Um einen Verweis auf System.Web.dll hinzufügen zu können, müssen Sie Folgendes tun rushonerok's Ratschläge : Stellen Sie sicher, dass das Ziel-Framework ".NET Framework 4" und nicht ".NET Framework 4 Client Profile" ist.

2 Stimmen

ASP.NET Mvc Schule ;)

12voto

Rosdi Kasim Punkte 22151

In C# 7 können Sie jetzt Tupel verwenden!... was die Notwendigkeit beseitigt, eine Klasse zu erstellen, nur um das Ergebnis zurückzugeben.

Hier ist ein Beispielcode:

public List<(string Name, string BreedName)> 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
             }.ToList();

    return result.Select(r => (r.Name, r.BreedName)).ToList();
}

Möglicherweise müssen Sie jedoch das Nuget-Paket System.ValueTuple installieren.

2 Stimmen

Sie brauchen ToList() nicht, da dies eine Abfrageauswertung erzwingt und die Liste im Speicher zuweist. Verwenden Sie einfach IEnumerable/IQueryable als Rückgabetyp und ToList() ist überhaupt nicht erforderlich. Ich verwende gerne Tupel, daher können Sie ab c# 7 einfach ein Tupel zurückgeben: IQueryable<(string Name, string BreedName)>, dann in der linq-Abfrage select (d.Name, b.BreedName). Dann brauchen Sie die Mehrfachauswahl nicht.

10voto

Hakan KOSE Punkte 731

Sie müssen Folgendes verwenden ToList() Methode, um Zeilen aus der Datenbank zu übernehmen und dann Elemente als Klasse auszuwählen. Versuchen Sie dies:

public partial class Dog {
    public string BreedName  { get; set; }}

List<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
                  }).ToList()
                    .Select(x=> 
                          new Dog{
                              Name = x.Name,
                              BreedName = x.BreedName,
                          }).ToList();
return result;}

Der Trick ist also erste ToList() . Es macht sofort die Abfrage und holt die Daten aus der Datenbank. Der zweite Trick ist Auswahl von Objekten und Verwendung des Objektinitialisierers um neue Objekte mit geladenen Gegenständen zu erzeugen.

Ich hoffe, das hilft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X