Поделиться через


Практическое руководство. Выполнение пользовательских операций соединения (Руководство по программированию на C#)

Обновлен: Ноябрь 2007

В этом примере показано, как выполнить операции соединения, которые нельзя осуществить с предложением join. В выражении запроса предложение join ограничено уравнивающими соединениями и оптимизировано для них; они соединения являются самым общим типом операции соединения. При выполнении уравнивающего соединения использование предложения join, возможно поможет обеспечить наилучшую производительность.

Однако в некоторых случаях предложение join неприемлемо.

  • Соединение объявлено в выражении неравенства.

  • Соединение объявлено в нескольких выражениях равенства или неравенства.

  • Необходимость создания временной переменной диапазона для правосторонней (внутренней) последовательности перед операцией соединения.

Чтобы независимо представить каждый источник данных, при выполнении соединений, не являющихся уравнивающими, можно использовать несколько предложений from. Затем переменной диапазона для каждого источника можно применить выражение предиката в предложении where. Выражение может иметь форму вызова метода.

Bb882533.alert_note(ru-ru,VS.90).gifПримечание.

Не путайте данный вид пользовательской операции соединения с использованием нескольких предложений from для доступа к внутренним коллекциям. Дополнительные сведения см. в разделе Предложение join (Справочник по C#).

Пример

Первый метод в следующем примере показывает простое перекрестное соединение. Перекрестные соединения следует использовать с осторожностью, поскольку они могут вернуть очень большие наборы результатов. Однако такие соединения могут быть полезны при создании исходных последовательностей, относительно которых выполняются дополнительные запросы.

Результатом второго метода является последовательность всех продуктов, идентификатор категории которых находится в списке категорий с правой стороны. Учтите необходимость использование предложения let и метода Contains для создания временного массива. Кроме того, массив можно создать до запроса и исключить первое предложение from.

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

В следующем примере запрос должен быть соединен с двумя последовательностями на основе сопоставления ключей, который в случае с внутренней (правосторонней) последовательностью, не может быть получен до предложения соединения. Если соединение было выполнено с помощью предложения join, для каждого элемента потребуется вызвать метод разделения. Использование нескольких предложений from позволяет запросу избежать издержек, связанных с повторным вызовом метода. Однако поскольку соединение оптимизировано, в данном случае его использование может быть эффективнее нескольких предложений from. Результаты будут зависеть в основном от затрат на вызов метода.

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

Этот пример также находится в статье Практическое руководство. Заполнение коллекций объектов из нескольких источников (LINQ). Для компиляции и выполнения примера следуйте инструкциям, приведенным в статье.

Компиляция кода

  • Создайте проект Visual Studio, предназначенный для платформы .NET Framework версии 3.5. По умолчанию в этом проекте имеется ссылка на файл System.Core.dll и директива using для пространства имен System.Linq.

  • Скопируйте код в созданный проект.

  • Нажмите клавишу F5, чтобы скомпилировать и запустить программу.

  • Нажмите любую клавишу для выхода из окна консоли.

См. также

Задачи

Практическое руководство. Упорядочение результатов предложения соединения (Руководство по программированию на C#)

Основные понятия

Выражения запросов LINQ (Руководство по программированию в C#)

Операции соединения

Ссылки

Предложение join (Справочник по C#)