Ausführen von benutzerdefinierten Verknüpfungsoperationen

In diesem Beispiel wird gezeigt, wie Verknüpfungsoperationen ausgeführt werden, die nicht mit der join-Klausel möglich sind. In einem Abfrageausdruck ist die join-Klausel auf Gleichheitsverknüpfungen, den bei weitem häufigsten Typ einer Verknüpfungsoperation, beschränkt und dafür optimiert. Wenn eine Gleichheitsverknüpfung ausgeführt wird, erhalten Sie die beste Leistung wahrscheinlich immer mit der join-Klausel.

Die join-Klausel kann jedoch nicht in den folgenden Fällen verwendet werden:

  • Wenn die Verknüpfung auf einem Ausdruck der Ungleichheit basiert (eine Ungleichheitsverknüpfung)

  • Wenn die Verknüpfung auf mehr als einem Ausdruck der Gleich- oder Ungleichheit basiert

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

Sie können zum Ausführen von Verknüpfungen, die keine Gleichheitsverknüpfungen sind, mehrere from-Klauseln verwenden, um jede Datenquelle einzeln einzuführen. Sie wenden anschließend 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 der benutzerdefinierten Verknüpfungsoperationen nicht mit der Verwendung mehrerer from-Klauseln, um auf innere Auflistungen zuzugreifen. Weitere Informationen finden Sie unter Join-Klausel.

Beispiel

Die erste Methode im folgenden Beispiel zeigt eine einfache Kreuzverknüpfung. Kreuzverknüpfungen müssen mit Bedacht verwendet werden, da sie einen sehr großen Ergebnissatz erzeugen können. Sie können jedoch in manchen Szenarios nützlich sein, um Quellsequenzen zu erstellen, mit denen zusätzliche Abfragen ausgeführt werden.

Die zweite Methode erzeugt eine Sequenz aller Produkte, deren Kategorie-ID in der Kategorieliste auf der linken Seite aufgeführt ist. Beachten Sie, dass Sie mit der Verwendung der let-Klausel und der Contains-Methode ein temporäres Array erstellen. Es ist auch möglich, das Array vor der Abfrage zu erstellen und die erste from-Klausel zu löschen.

     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($"{v.ID:-5}{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($"{v.CategoryID:-5}{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.
      */

Beispiel

Im folgenden Beispiel muss die Abfrage zwei Sequenzen anhand übereinstimmender Schlüssel verknüpfen, die bei der inneren (rechten) Sequenz nicht vor der eigentlichen Join-Klausel abgerufen werden können. Wenn diese Verknüpfung mit einer join-Klausel ausgeführt werden würde, müsste anschließend die Split-Methode für jedes Element aufgerufen werden. Mit der Verwendung von mehreren from-Klauseln kann die Abfrage den Mehraufwand des wiederholten Methodenaufrufs vermeiden. Da join jedoch optimiert ist, könnte es in diesem Sonderfall trotzdem schneller sein als mehrere from-Klauseln zu verwenden. Die Ergebnisse variieren hauptsächlich nach Aufwand des Methodenaufrufs.

class MergeTwoCSVFiles
{
    static void Main()
    {
        // See section Compiling the Code for information about the data files.
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type.
        // You could use var instead of an explicit type for the query.
        IEnumerable<Student> queryNamesScores =
            // Split each line in the data files into an array of strings.
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            // Look for matching IDs from the two data files.
            where x[2] == s[0]
            // If the IDs match, build a Student object.
            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 {student.FirstName} {student.LastName} is 
                {student.ExamScores.Average()}.");
        }

        //Keep console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int ID { get; set; }
    public List<int> ExamScores { get; set; }
}

/* Output: 
    The average score of Omelchenko Svetlana is 82.5.
    The average score of O'Donnell Claire is 72.25.
    The average score of Mortensen Sven is 84.5.
    The average score of Garcia Cesar is 88.25.
    The average score of Garcia Debra is 67.
    The average score of Fakhouri Fadi is 92.25.
    The average score of Feng Hanying is 88.
    The average score of Garcia Hugo is 85.75.
    The average score of Tucker Lance is 81.75.
    The average score of Adams Terry is 85.25.
    The average score of Zabokritski Eugene is 83.
    The average score of Tucker Michael is 92.
 */

Siehe auch

LINQ-Abfrageausdrücke
Join-Klausel
Sortieren der Ergebnisse einer Join-Klausel