LINQ内连接

我有两个系列:

List ids; List users; 

User有id,名称等的地方

我想内部连接这两个集合并返回一个新的List ,其中包含来自第一个集合的ID,这些集合也位于第二个集合中(用户ID)。

我是LINQ的新手,不知道从哪里开始。

谢谢。

您不需要使用join来执行此操作:

 List commonIds = ids.Intersect(users.Select(u => u.Id)).ToList(); 

编辑:在回复评论中的问题时,您可以在不使用Join情况下获得用户列表:

 var matchingUsers = users.Where(u => ids.Contains(u.Id)); 

然而,这是非常低效的,因为Where子句必须扫描每个用户的id列表。 我认为加入将是处理这种情况的最佳方式:

 List matchingUsers = users.Join(ids, u => u.Id, id => id, (user, id) => user).ToList(); 

在关系数据库术语中,内部联接生成结果集,其中第一个集合的每个元素对于第二个集合中的每个匹配元素都出现一次。 如果第一个集合中的元素没有匹配的元素,则它不会出现在结果集中。 Join方法由C#中的join子句调用,实现内连接。

本主题介绍如何执行内部联接的四种变体:

  • 一个简单的内部联接,它基于一个简单的密钥关联来自两个数据源的元素。

  • 内部联接,它基于组合键关联来自两个数据源的元素。 复合键是一个由多个值组成的键,使您可以基于多个属性关联元素。

  • 多个连接,其中连续的连接操作相互附加。

  • 通过使用组连接实现的内部联接。

示例简单键连接示例

以下示例创建两个集合,其中包含两个用户定义类型Person和Pet的对象。 该查询使用C#中的join子句将Person对象与其Owner为Person的Pet对象进行匹配。 C#中的select子句定义了结果对象的外观。 在此示例中,生成的对象是匿名类型,由所有者的名字和宠物的名称组成。 C#

 class Person { public string FirstName { get; set; } public string LastName { get; set; } } class Pet { public string Name { get; set; } public Person Owner { get; set; } } ///  /// Simple inner join. ///  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 people = new List { magnus, terry, charlotte, arlene, rui }; List pets = new List { 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 

请注意,LastName为“Huff”的Person对象不会出现在结果集中,因为没有Pet对象的Pet.Owner等于该Person。 示例复合键连接示例

您可以使用复合键基于多个属性比较元素,而不是仅基于一个属性关联元素。 为此,请为每个集合指定键选择器函数,以返回包含要比较的属性的匿名类型。 如果标记属性,则每个键的匿名类型必须具有相同的标签。 属性也必须以相同的顺序出现。

以下示例使用Employee对象列表和Student对象列表来确定哪些员工也是学生。 这两种类型都具有String类型的FirstName和LastName属性。 从每个列表的元素创建连接键的函数返回一个匿名类型,该类型由每个元素的FirstName和LastName属性组成。 连接操作将这些复合键进行相等性比较,并返回每个列表中的对象对,其中第一个名称和姓氏都匹配。 C#

 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; } } ///  /// Performs a join operation using a composite key. ///  public static void CompositeKeyJoinExample() { // Create a list of employees. List employees = new List { 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 students = new List { 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 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 

示例多个连接示例

可以将任意数量的连接操作彼此附加以执行多连接。 C#中的每个连接子句将指定的数据源与先前连接的结果相关联。

以下示例创建三个集合:Person对象列表,Cat对象列表和Dog对象列表。

C#中的第一个join子句根据与Cat.Owner匹配的Person对象匹配人和猫。 它返回一系列包含Person对象和Cat.Name的匿名类型。

C#中的第二个连接子句将第一个连接返回的匿名类型与提供的狗列表中的Dog对象相关联,这些类型基于由Person类型的Owner属性和动物名称的第一个字母组成的复合键。 它返回一系列匿名类型,其中包含每个匹配对的Cat.Name和Dog.Name属性。 因为这是一个内连接,所以只返回第一个数据源中第二个数据源中匹配的对象。 C#

 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 people = new List { magnus, terry, charlotte, arlene, rui, phyllis }; List cats = new List { barley, boots, whiskers, bluemoon, daisy }; List dogs = new List { 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". 

使用分组连接示例进行内连接示例

以下示例说明如何使用组连接实现内部联接。

在query1中,Person对象列表基于与Pet.Owner属性匹配的Person组连接到Pet对象列表。 组连接创建一组中间组,其中每个组由一个Person对象和一系列匹配的Pet对象组成。

通过向查询添加第二个from子句,将该序列序列组合(或展平)为一个更长的序列。 最终序列的元素类型由select子句指定。 在此示例中,该类型是匿名类型,由每个匹配对的Person.FirstName和Pet.Name属性组成。

query1的结果等同于使用join子句而不使用into子句来执行内连接所获得的结果集。 query2变量演示此等效查询。 C#

 class Person { public string FirstName { get; set; } public string LastName { get; set; } } class Pet { public string Name { get; set; } public Person Owner { get; set; } } ///  /// Performs an inner join by using GroupJoin(). ///  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 people = new List { magnus, terry, charlotte, arlene }; List pets = new List { 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 

编写代码

  • 在Visual Studio中创建一个新的控制台应用程序项目。

  • 如果尚未引用System.Core.dll,请添加对它的引用。

  • 包括System.Linq命名空间。

  • 将示例中的代码复制并粘贴到Main方法下面的program.cs文件中。 在Main方法中添加一行代码以调用您粘贴的方法。

  • 运行程序。