Porady: wykonywanie niestandardowych operacji łączenia (Przewodnik programowania w języku C#)

Ten przykład pokazuje sposób wykonywania operacji sprzężenia, które nie są możliwe przy join klauzuli.W wyrażeniu kwerendy join klauzuli jest ograniczona do i zoptymalizowany pod kątem, typu equijoins, które są najbardziej typowych operacji join.Podczas wykonywania sprzężenie, będzie prawdopodobnie zawsze uzyskać najlepszą wydajność przy użyciu join klauzuli.

Jednakże join klauzuli nie można używać w następujących przypadkach:

  • Kiedy sprzężenia jest uzależniona od wyrażenia nierówność (non-equijoin).

  • Po sprzężeniu opiera się na więcej niż jedno wyrażenie równości lub nierówności.

  • Kiedy masz wprowadzenie zmiennej tymczasowej zakres sekwencji po prawej stronie (wewnętrzne) przed wykonaniem operacji join.

Aby przeprowadzić sprzężenia, które nie są equijoins, można użyć wielu from klauzul wprowadzenie niezależnie dla każdego źródła danych.Następnie zastosować predykatu wyrażenie w where klauzuli do zmiennej zakres dla każdego źródła.Wyrażenie może również przyjąć formę wywołania metody.

[!UWAGA]

Nie należy mylić tego rodzaju operacji join niestandardowe przy użyciu wielu from klauzul, które mają dostęp do wewnętrznej kolekcji.Aby uzyskać więcej informacji, zobacz Klauzula join (odwołanie w C#).

Przykład

Pierwsza metoda w poniższym przykładzie przedstawiono proste sprzężenia krzyżowego.Sprzężenia krzyżowe należy używać ostrożnie, ponieważ mogą one powodować bardzo duży zestaw wyników.Jednakże mogą być użyteczne w niektórych scenariuszach dla tworzenia sekwencji źródła, wobec których wykonywania dodatkowych kwerend.

Druga metoda daje sekwencji wszystkich produktów, których identyfikator kategorii jest wymienione na liście kategorii po lewej stronie.Uwaga Użycie let klauzuli i Contains metodę tworzenia tablicy tymczasowej.Także możliwe jest tworzenie tablicy przed kwerendy i wyeliminować pierwsze from klauzuli.

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.
      */

W poniższym przykładzie kwerendy należy przyłączyć się do dwóch sekwencji na podstawie pasujące klucze, które z sekwencji wewnętrzne (po prawej stronie) nie może być uzyskane przed klauzuli join, sam.Jeśli tego sprzężenia zostały wykonane z join klauzulę, a następnie Split Metoda musiałaby zostać wywołana dla każdego elementu.Użycie wielu from klauzul umożliwia kwerendy uniknąć napowietrznej wywołanie metody powtarzających się.Ponieważ jednak join jest zoptymalizowana w tym szczególnym przypadku, to wciąż może być szybciej niż przy użyciu wielu from klauzul.Wyniki będą się różnić przede wszystkim zależności jak drogie jest wywołanie metody.

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 {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();
    }
}

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.
 */

Kompilowanie kodu

  • Tworzenie Visual Studio projekt aplikacji konsoli, który jest przeznaczony dla .NET Framework 3.5 lub nowszego.Domyślnie projekt zawiera odwołanie do System.Core.dll i using dyrektywa obszaru nazw System.Linq.

  • Zastąpić Program klasy z kodem w poprzednim przykładzie.

  • Postępuj zgodnie z instrukcjami w Porady: łączenie zawartości niepodobnych plików (LINQ) do ustawiania plików danych, scores.csv i names.csv.

  • Naciśnij klawisz F5, aby skompilować i uruchomić program.

  • Naciśnij dowolny klawisz, aby zamknąć okno konsoli.

Zobacz też

Zadania

Porady: kolejność wyników klauzuli join (Przewodnik programowania w języku C#)

Informacje

Klauzula join (odwołanie w C#)

Koncepcje

Wyrażenia kwerend LINQ (Przewodnik programowania w języku C#)

Operacje połączone