from-Klausel (C#-Referenz)

Ein Abfrageausdruck muss mit einer from-Klausel beginnen. Darüber hinaus kann ein Abfrageausdruck Unterabfragen enthalten, die auch mit einer from-Klausel beginnen. Die from-Klausel gibt Folgendes an:

  • Die Datenquelle, für die die Abfrage oder Unterabfrage ausgeführt wird.

  • Eine lokale Bereichsvariable, die jedes Element in der Quellsequenz darstellt.

Sowohl die Bereichsvariable als auch die Datenquelle sind stark typisiert. Die Datenquelle, auf die in der from-Klausel verwiesen wird, muss vom Typ IEnumerable, IEnumerable<T> oder von einem abgeleiteten Typ wie IQueryable<T> sein.

Im folgenden Beispiel numbers ist die Datenquelle und num ist die Bereichsvariable. Beachten Sie, dass beide Variablen stark typisiert sind, obwohl das var-Schlüsselwort verwendet wird.

class LowNums
{
    static void Main()
    {
        // A simple data source.
        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

        // Create the query.
        // lowNums is an IEnumerable<int>
        var lowNums = from num in numbers
            where num < 5
            select num;

        // Execute the query.
        foreach (int i in lowNums)
        {
            Console.Write(i + " ");
        }
    }
}
// Output: 4 1 3 2 0

Die Bereichsvariable

Der Compiler leitet den Typ der Bereichsvariablen ab, wenn die Datenquelle IEnumerable<T> implementiert. Wenn die Quelle beispielsweise vom Typ IEnumerable<Customer> ist, wird die Bereichsvariable als Customer abgeleitet. Sie müssen den Typ nur explizit angeben, wenn die Quelle ein nicht-generischer IEnumerable-Typ wie z.B. ArrayList ist. Weitere Informationen finden Sie unter Vorgehensweise: Abfragen von ArrayList mit LINQ.

Im vorherigen Beispiel wird num als Typ int abgeleitet. Da die Bereichsvariable stark typisiert ist, können Sie für sie Methoden aufrufen oder sie in anderen Vorgängen verwenden. Anstatt z.B. select num zu schreiben, könnten Sie select num.ToString() schreiben, sodass der Abfrageausdruck eine Sequenz von Zeichenfolgen anstelle von Ganzzahlen zurückgibt. Sie könnten auch select num + 10 schreiben, damit der Ausdruck die Sequenz „14, 11, 13, 12, 10“ zurückgibt. Weitere Informationen finden Sie unter select clause (select-Klausel).

Die Bereichsvariable entspricht einer Iterationsvariablen in einer foreach-Anweisung mit einer wichtigen Ausnahme: Eine Bereichsvariable speichert niemals Daten aus der Quelle. Sie ist nur ein syntaktisches Hilfsmittel, mit dem die Abfrage beschreiben kann, was eintritt, wenn die Abfrage ausgeführt wird. Weitere Informationen finden Sie unter Introduction to LINQ queries (C#) (Einführung in LINQ-Abfragen (C#)).

Zusammengesetzte from-Klauseln

In einigen Fällen kann jedes Element in der Quellsequenz selbst eine Sequenz sein oder eine Sequenz enthalten. Ihre Datenquelle kann beispielsweise ein IEnumerable<Student> sein, wobei jedes Student-Objekt in der Sequenz eine Liste der Testergebnisse enthält. Sie können zusammengesetzte from-Klauseln verwenden, um auf die innere Liste jedes Student-Objekts zuzugreifen. Die Technik entspricht dem Verwenden von geschachtelten foreach-Anweisungen. Sie können die where-Klausel oder die orderby-Klausel zu einer der from-Klauseln hinzufügen, um die Ergebnisse zu filtern. Das folgende Beispiel enthält eine Sequenz von Student-Objekten, von denen jedes eine innere List mit Ganzzahlen enthält, die die Testergebnisse darstellen. Um auf die innere Liste zuzugreifen, verwenden Sie eine zusammengesetzte from-Klausel. Bei Bedarf können Sie Klauseln zwischen den beiden from-Klauseln einfügen.

class CompoundFrom
{
    // The element type of the data source.
    public class Student
    {
        public required string LastName { get; init; }
        public required List<int> Scores {get; init;}
    }

    static void Main()
    {

        // Use a collection initializer to create the data source. Note that
        // each element in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {LastName="Omelchenko", Scores= [97, 72, 81, 60]},
           new Student {LastName="O'Donnell", Scores= [75, 84, 91, 39]},
           new Student {LastName="Mortensen", Scores= [88, 94, 65, 85]},
           new Student {LastName="Garcia", Scores= [97, 89, 85, 82]},
           new Student {LastName="Beebe", Scores= [35, 72, 91, 70]}
        ];

        // Use a compound from to access the inner sequence within each element.
        // Note the similarity to a nested foreach statement.
        var scoreQuery = from student in students
                         from score in student.Scores
                            where score > 90
                            select new { Last = student.LastName, score };

        // Execute the queries.
        Console.WriteLine("scoreQuery:");
        // Rest the mouse pointer on scoreQuery in the following line to
        // see its type. The type is IEnumerable<'a>, where 'a is an
        // anonymous type defined as new {string Last, int score}. That is,
        // each instance of this anonymous type has two members, a string
        // (Last) and an int (score).
        foreach (var student in scoreQuery)
        {
            Console.WriteLine("{0} Score: {1}", student.Last, student.score);
        }
    }
}
/*
scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91
*/

Verwenden von mehreren from-Klauseln zum Ausführen von Joins

Eine zusammengesetzte from-Klausel wird zum Zugriff auf innere Auflistungen in einer einzelnen Datenquelle verwendet. Eine Abfrage kann jedoch auch mehrere from-Klauseln enthalten, die ergänzende Abfragen aus unabhängigen Datenquellen generieren. Mit dieser Technik können Sie bestimmte Typen von Verknüpfungsvorgängen durchführen, die beim Einsatz der join-Klausel nicht möglich sind.

Das folgende Beispiel veranschaulicht, wie zwei from-Klauseln verwendet werden können, um einen vollständigen Cross Join zweier Datenquellen zu bilden.

class CompoundFrom2
{
    static void Main()
    {
        char[] upperCase = ['A', 'B', 'C'];
        char[] lowerCase = ['x', 'y', 'z'];

        // The type of joinQuery1 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };

        // The type of joinQuery2 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };

        // Execute the queries.
        Console.WriteLine("Cross join:");
        // Rest the mouse pointer on joinQuery1 to verify its type.
        foreach (var pair in joinQuery1)
        {
            Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
        }

        Console.WriteLine("Filtered non-equijoin:");
        // Rest the mouse pointer over joinQuery2 to verify its type.
        foreach (var pair in joinQuery2)
        {
            Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */

Weitere Informationen zu Verknüpfungsvorgängen mit mehreren from-Klauseln finden Sie unter Ausführen von Left Outer Joins.

Weitere Informationen