Gewusst wie: Ausführen von benutzerdefinierten Verknüpfungsoperationen (C#-Programmierhandbuch)

Aktualisiert: November 2007

In diesem Beispiel wird gezeigt, wie Sie Verknüpfungsoperationen, die mit der join-Klausel nicht möglich sind, ausführen können. In einem Abfrageausdruck ist die join-Klausel auf Gleichheitsverknüpfungen beschränkt und wurde für sie optimiert. Gleichheitsverknüpfungen sind der häufigste Typ an Verknüpfungsoperationen. Wenn Sie eine Gleichheitsverknüpfung ausführen, erhalten Sie mit der join-Klausel wahrscheinlich immer die optimale Leistung.

Es gibt jedoch einige Fälle, in denen die join-Klausel nicht verwendet werden kann:

  • Wenn die Verknüpfung auf einem Ausdruck der Ungleichheit (eine Nicht-Gleichheitsverknüpfung) basiert wird.

  • Wenn die Verknüpfung auf mehr als einem Ausdruck der Gleichheit oder Ungleichheit basiert wird.

  • Wenn Sie eine temporäre Bereichsvariable für die rechte (innere) Sequenz vor der Verknüpfungsoperation verwenden müssen.

Um Verknüpfungen auszuführen, bei denen es sich nicht um Gleichheitsverknüpfungen handelt, können Sie mehrere from-Klauseln verwenden, um jede Datenquelle einzeln einzuführen. Sie wenden dann einen Prädikatsausdruck in einer where-Klausel auf die Bereichsvariable für jede Quelle an. Der Ausdruck kann auch die Form eines Methodenaufrufs annehmen.

Hinweis:

Verwechseln Sie diese Art von benutzerdefinierten Verknüpfungsoperationen nicht mit dem Einsatz von mehreren from-Klauseln, um auf innere Sammlungen zuzugreifen. Weitere Informationen finden Sie unter join-Klausel (C#-Referenz).

Beispiel

Die erste Methode im folgenden Beispiel zeigt eine einfache Kreuzverknüpfung. Kreuzverknüpfungen müssen mit Vorsicht verwendet werden, da sie sehr große Ergebnissätze erzeugen können. Sie können jedoch in einigen Szenarien zum Erstellen von Quellsequenzen, gegen die zusätzliche Abfragen ausgeführt werden, nützlich sein.

Die zweite Methode erzeugt eine Sequenz aller Produkte, deren Kategorie-ID in der Kategorieliste links aufgeführt ist. Beachten Sie die Verwendung der let-Klausel und der Contains-Methode, um ein temporäres Array zu erstellen. Sie könnten das Array auch vor der Abfrage erstellen und die erste from-Klausel ausschließen.

    class CustomJoins
    {

        #region Data

        class Product
        {
            public string Name { get; set; }
            public int CategoryID { get; set; }
        }

        class Category
        {
            public string Name { get; set; }
            public int ID { get; set; }
        }

        // Specify the first data source.
        List<Category> categories = new List<Category>()
        { 
            new Category(){Name="Beverages", ID=001},
            new Category(){ Name="Condiments", ID=002},
            new Category(){ Name="Vegetables", ID=003},         
        };

        // Specify the second data source.
        List<Product> products = new List<Product>()
       {
          new Product{Name="Tea",  CategoryID=001},
          new Product{Name="Mustard", CategoryID=002},
          new Product{Name="Pickles", CategoryID=002},
          new Product{Name="Carrots", CategoryID=003},
          new Product{Name="Bok Choy", CategoryID=003},
          new Product{Name="Peaches", CategoryID=005},
          new Product{Name="Melons", CategoryID=005},
          new Product{Name="Ice Cream", CategoryID=007},
          new Product{Name="Mackerel", CategoryID=012},
        };
        #endregion

        static void Main()
        {
            CustomJoins app = new CustomJoins();
            app.CrossJoin();
            app.NonEquijoin();

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        void CrossJoin()
        {
            var crossJoinQuery = 
                from c in categories
                from p in products
                select new { c.ID, p.Name };

            Console.WriteLine("Cross Join Query:");
            foreach (var v in crossJoinQuery)
            {
                Console.WriteLine("{0,-5}{1}", v.ID, v.Name);
            }            
        }

        void NonEquijoin()
        {
            var nonEquijoinQuery = 
                from p in products
                let catIds = from c in categories
                             select c.ID
                where catIds.Contains(p.CategoryID) == true
                select new { Product = p.Name, CategoryID = p.CategoryID };

            Console.WriteLine("Non-equijoin query:");
            foreach (var v in nonEquijoinQuery)
            {
                Console.WriteLine("{0,-5}{1}",  v.CategoryID, v.Product);
            }
        }
    }
    /* Output:
Cross Join Query:
1    Tea
1    Mustard
1    Pickles
1    Carrots
1    Bok Choy
1    Peaches
1    Melons
1    Ice Cream
1    Mackerel
2    Tea
2    Mustard
2    Pickles
2    Carrots
2    Bok Choy
2    Peaches
2    Melons
2    Ice Cream
2    Mackerel
3    Tea
3    Mustard
3    Pickles
3    Carrots
3    Bok Choy
3    Peaches
3    Melons
3    Ice Cream
3    Mackerel
Non-equijoin query:
1    Tea
2    Mustard
2    Pickles
3    Carrots
3    Bok Choy
Press any key to exit.
     */

Im folgenden Beispiel muss die Abfrage zwei Sequenzen anhand übereinstimmender Schlüssel verknüpfen, die im Fall der inneren (rechten) Sequenz nicht vor der join-Klausel selbst abgerufen werden können. Wenn diese Verknüpfung mit einer join-Klausel ausgeführt wurde, muss die Split-Methode für jedes Element aufgerufen werden. Der Einsatz von mehreren from-Klauseln ermöglicht es der Abfrage, den Aufwand eines wiederholten Methodenaufrufs zu verhindern. Da die Verknüpfung optimiert ist, ist dies in diesem speziellen Fall vermutlich dennoch schneller als der Einsatz mehrerer from-Klauseln. Die Ergebnisse ändern sich hauptsächlich in Abhängigkeit davon, wie teuer der Methodenaufruf ist.

class MergeTwoCSVFiles : StudentClass
{
    static void Main()
    {
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type
        // var could be used instead of an explicit type
        IEnumerable<Student> queryNamesScores =
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            where x[2] == s[0]
            select new Student()
            {
                FirstName = x[0],
                LastName = x[1],
                ID = Convert.ToInt32(x[2]),
                ExamScores = (from scoreAsText in s.Skip(1)
                              select Convert.ToInt32(scoreAsText)).
                              ToList()
            };

        // Optional. Store the newly created student objects in memory
        // for faster access in future queries
        List<Student> students = queryNamesScores.ToList();

        foreach (var student in students)
        {
            Console.WriteLine("The average score of {0} {1} is {2}.",
                student.FirstName, student.LastName, student.ExamScores.Average());
        }

        //Keep console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output: 
    The average score of Adams Terry is 85.25.
    The average score of Fakhouri Fadi is 92.25.
    The average score of Feng Hanying is 88.
    The average score of Garcia Cesar is 88.25.
    The average score of Garcia Debra is 67.
    The average score of Garcia Hugo is 85.75.
    The average score of Mortensen Sven is 84.5.
    The average score of O'Donnell Claire is 72.25.
    The average score of Omelchenko Svetlana is 82.5.
    The average score of Tucker Lance is 81.75.
    The average score of Tucker Michael is 92.
    The average score of Zabokritski Eugene is 83.
 */

Dieses Beispiel wird auch im Artikel Gewusst wie: Füllen von Objektauflistungen aus mehreren Quellen (LINQ) aufgeführt. Befolgen Sie die Anweisungen in diesem Artikel, um das Beispiel zu kompilieren und auszuführen.

Kompilieren des Codes

  • Erstellen Sie ein Visual Studio-Projekt für .NET Framework, Version 3.5. Standardmäßig hat das Projekt einen Verweis auf System.Core.dll und eine using-Direktive für den System.Linq-Namespace.

  • Kopieren Sie den Code in Ihr Projekt.

  • Drücken Sie F5, um das Programm zu kompilieren und auszuführen.

  • Drücken Sie eine beliebige Taste, um das Konsolenfenster zu schließen.

Siehe auch

Aufgaben

Gewusst wie: Sortieren der Ergebnisse einer Join-Klausel (C#-Programmierhandbuch)

Konzepte

LINQ-Abfrageausdrücke (C#-Programmierhandbuch)

Verknüpfungsvorgänge

Referenz

join-Klausel (C#-Referenz)