Cómo: Realizar combinaciones internas (Guía de programación de C#)

En términos de bases de datos relacionales, una combinación interna genera un conjunto de resultados en el que cada elemento de la primera colección aparece una vez para cada elemento correspondiente de la segunda colección.Si un elemento de la primera colección no tiene ningún elemento correspondiente, no aparece en el conjunto de resultados.El método Join, que se invoca mediante la cláusula join en C#, implementa una combinación interna.

En este tema se muestra cómo realizar cuatro variaciones de una combinación interna:

  • Una combinación interna simple que correlaciona elementos de dos orígenes de datos según una clave simple.

  • Una combinación interna que correlaciona elementos de dos orígenes de datos según una clave compuesta .Una clave compuesta, que es una clave que se compone de más de un valor, permite correlacionar elementos según más de una propiedad.

  • Una combinación múltiple en la que se anexan operaciones de combinación sucesivas.

  • Una combinación interna que se implementa mediante una combinación agrupada.

Ejemplo

Ejemplo de combinación de clave simple

En el ejemplo siguiente se crean dos colecciones que contienen objetos de dos tipos definidos por el usuario, Person y Pet.La consulta usa la cláusula join en C# para establecer correspondencias entre los objetos Person y los objetos Pet cuyo Owner es ese objeto Person.La cláusula select en C# define el aspecto de los objetos resultantes.En este ejemplo, los objetos resultantes son tipos anónimos que consisten en el nombre del propietario y el nombre del animal doméstico.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        /// <summary>
        /// Simple inner join.
        /// </summary>
        public static void InnerJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
            Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };

            Pet barley = new Pet { Name = "Barley", Owner = terry };
            Pet boots = new Pet { Name = "Boots", Owner = terry };
            Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
            Pet bluemoon = new Pet { Name = "Blue Moon", Owner = rui };
            Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene, rui };
            List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

            // Create a collection of person-pet pairs. Each element in the collection
            // is an anonymous type containing both the person's name and their pet's name.
            var query = from person in people
                        join pet in pets on person equals pet.Owner
                        select new { OwnerName = person.FirstName, PetName = pet.Name };

            foreach (var ownerAndPet in query)
            {
                Console.WriteLine("\"{0}\" is owned by {1}", ownerAndPet.PetName, ownerAndPet.OwnerName);
            }
        }

        // This code produces the following output:
        //
        // "Daisy" is owned by Magnus
        // "Barley" is owned by Terry
        // "Boots" is owned by Terry
        // "Whiskers" is owned by Charlotte
        // "Blue Moon" is owned by Rui

Observe cómo el objeto Person cuyo LastName es "Huff" no aparece en el conjunto de resultados porque no es ningún objeto Pet cuyo Pet.Owner sea igual a ese objeto Person.

Ejemplo de combinación de clave compuesta

En lugar de correlacionar elementos según una sola propiedad, puede utilizar una clave compuesta para comparar los elementos según varias propiedades.Para ello, especifique la función del selector de claves para cada colección para devolver un tipo anónimo formado por las propiedades que desea comparar.Si etiqueta las propiedades, deben tener la misma etiqueta en el tipo anónimo de cada clave.Las propiedades también deben aparecer en el mismo orden.

En el ejemplo siguiente se utiliza una lista de objetos Employee y una lista de objetos Student para determinar qué empleados son también estudiantes.Ambos tipos tienen una propiedad FirstName y LastName de tipo String.Las funciones que crean las claves de combinación a partir de los elementos de cada lista devuelven un tipo anónimo formado por las propiedades FirstName y LastName de cada elemento.La operación de combinación compara la igualdad de estas claves compuestas y devuelve pares de objetos de cada lista que tengan el mismo nombre y apellido.

        class Employee
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int EmployeeID { get; set; }
        }

        class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int StudentID { get; set; }
        }

        /// <summary>
        /// Performs a join operation using a composite key.
        /// </summary>
        public static void CompositeKeyJoinExample()
        {
            // Create a list of employees.
            List<Employee> employees = new List<Employee> {
                new Employee { FirstName = "Terry", LastName = "Adams", EmployeeID = 522459 },
                 new Employee { FirstName = "Charlotte", LastName = "Weiss", EmployeeID = 204467 },
                 new Employee { FirstName = "Magnus", LastName = "Hedland", EmployeeID = 866200 },
                 new Employee { FirstName = "Vernette", LastName = "Price", EmployeeID = 437139 } };

            // Create a list of students.
            List<Student> students = new List<Student> {
                new Student { FirstName = "Vernette", LastName = "Price", StudentID = 9562 },
                new Student { FirstName = "Terry", LastName = "Earls", StudentID = 9870 },
                new Student { FirstName = "Terry", LastName = "Adams", StudentID = 9913 } };

            // Join the two data sources based on a composite key consisting of first and last name,
            // to determine which employees are also students.
            IEnumerable<string> query = from employee in employees
                                        join student in students
                                        on new { employee.FirstName, employee.LastName }
                                        equals new { student.FirstName, student.LastName }
                                        select employee.FirstName + " " + employee.LastName;

            Console.WriteLine("The following people are both employees and students:");
            foreach (string name in query)
                Console.WriteLine(name);
        }

        // This code produces the following output:
        //
        // The following people are both employees and students:
        // Terry Adams
        // Vernette Price

Ejemplo de combinación múltiple

Es posible anexar cualquier número de operaciones de combinación para realizar una combinación múltiple.Cada cláusula join en C# relaciona un origen de datos especificado con los resultados de la combinación anterior.

En el ejemplo siguiente se crean tres colecciones: una lista de objetos Person, una lista de objetos Cat y una lista de objetos Dog.

En C#, la primera cláusula join establece correspondencias entre las personas y los gatos en función de las coincidencias entre Cat.Owner y Person.Devuelve una secuencia de tipos anónimos que contiene el objeto Person y Cat.Name.

En C#, la segunda cláusula join relaciona los tipos anónimos devueltos por la primera combinación con los objetos Dog de la lista de perros proporcionada en función de una clave compuesta formada por la propiedad Owner del tipo Person y la primera letra del nombre del animal.Devuelve una secuencia de tipos anónimos que contiene las propiedades Cat.Name y Dog.Name de cada par coincidente.Dado que se trata de una combinación interna, se devuelven sólo los objetos del primer origen de datos que tienen una correspondencia en el segundo origen de datos.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        class Cat : Pet
        { }

        class Dog : Pet
        { }

        public static void MultipleJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
            Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
            Person phyllis = new Person { FirstName = "Phyllis", LastName = "Harris" };

            Cat barley = new Cat { Name = "Barley", Owner = terry };
            Cat boots = new Cat { Name = "Boots", Owner = terry };
            Cat whiskers = new Cat { Name = "Whiskers", Owner = charlotte };
            Cat bluemoon = new Cat { Name = "Blue Moon", Owner = rui };
            Cat daisy = new Cat { Name = "Daisy", Owner = magnus };

            Dog fourwheeldrive = new Dog { Name = "Four Wheel Drive", Owner = phyllis };
            Dog duke = new Dog { Name = "Duke", Owner = magnus };
            Dog denim = new Dog { Name = "Denim", Owner = terry };
            Dog wiley = new Dog { Name = "Wiley", Owner = charlotte };
            Dog snoopy = new Dog { Name = "Snoopy", Owner = rui };
            Dog snickers = new Dog { Name = "Snickers", Owner = arlene };

            // Create three lists.
            List<Person> people =
                new List<Person> { magnus, terry, charlotte, arlene, rui, phyllis };
            List<Cat> cats =
                new List<Cat> { barley, boots, whiskers, bluemoon, daisy };
            List<Dog> dogs =
                new List<Dog> { fourwheeldrive, duke, denim, wiley, snoopy, snickers };

            // The first join matches Person and Cat.Owner from the list of people and
            // cats, based on a common Person. The second join matches dogs whose names start
            // with the same letter as the cats that have the same owner.
            var query = from person in people
                        join cat in cats on person equals cat.Owner
                        join dog in dogs on 
                        new { Owner = person, Letter = cat.Name.Substring(0, 1) }
                        equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) }
                        select new { CatName = cat.Name, DogName = dog.Name };

            foreach (var obj in query)
            {
                Console.WriteLine(
                    "The cat \"{0}\" shares a house, and the first letter of their name, with \"{1}\".", 
                    obj.CatName, obj.DogName);
            }
        }

        // This code produces the following output:
        //
        // The cat "Daisy" shares a house, and the first letter of their name, with "Duke".
        // The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley".

Ejemplo de combinación interna mediante una combinación agrupada

En el ejemplo siguiente se muestra cómo implementar una combinación interna mediante una combinación agrupada.

En query1, la lista de objetos Person se combina en grupo con la lista de objetos Pet según el objeto Person que coincide con la propiedad Pet.Owner.La combinación agrupada crea una colección de grupos intermedios, donde cada grupo está compuesto de un objeto Person y una secuencia de objetos Pet coincidentes.

Al agregar una segunda cláusula from a la consulta, esta secuencia de secuencias se combina (o simplifica) en una secuencia más larga.La cláusula select especifica el tipo de los elementos de la secuencia final.En este ejemplo, se trata de un tipo anónimo formado por las propiedades Person.FirstName y Pet.Name de cada par coincidente.

El resultado de query1 es equivalente al conjunto de resultados que se obtendrían usando la cláusula join sin la cláusula into para realizar una combinación interna.La variable query2 muestra esta consulta equivalente.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        /// <summary>
        /// Performs an inner join by using GroupJoin().
        /// </summary>
        public static void InnerGroupJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

            Pet barley = new Pet { Name = "Barley", Owner = terry };
            Pet boots = new Pet { Name = "Boots", Owner = terry };
            Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
            Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
            Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
            List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

            var query1 = from person in people
                         join pet in pets on person equals pet.Owner into gj
                         from subpet in gj
                         select new { OwnerName = person.FirstName, PetName = subpet.Name };

            Console.WriteLine("Inner join using GroupJoin():");
            foreach (var v in query1)
            {
                Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
            }

            var query2 = from person in people
                         join pet in pets on person equals pet.Owner
                         select new { OwnerName = person.FirstName, PetName = pet.Name };

            Console.WriteLine("\nThe equivalent operation using Join():");
            foreach (var v in query2)
                Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
        }

        // This code produces the following output:
        //
        // Inner join using GroupJoin():
        // Magnus - Daisy
        // Terry - Barley
        // Terry - Boots
        // Terry - Blue Moon
        // Charlotte - Whiskers
        //
        // The equivalent operation using Join():
        // Magnus - Daisy
        // Terry - Barley
        // Terry - Boots
        // Terry - Blue Moon
        // Charlotte - Whiskers

Compilar el código

  • Cree un nuevo proyecto de aplicación de consola en Visual Studio.

  • Agregue una referencia a System.Core.dll si ésta no existe aún.

  • Incluya el espacio de nombres System.Linq.

  • Copie el código del ejemplo y péguelo en el archivo program.cs , bajo el método Main.Agregue una línea de código al método Main para llamar al método que pegó.

  • Ejecute el programa.

Vea también

Tareas

Cómo: Realizar combinaciones agrupadas (Guía de programación de C#)

Cómo: Realizar operaciones de combinación externa izquierda (Guía de programación de C#)

How to: Join Two Collections (C#) LINQ to XML

Referencia

Join

GroupJoin

Tipos anónimos (Guía de programación de C#)

Conceptos

Operaciones de combinación

Tipos anónimos (Visual Basic)