Linq 用枢轴填充嵌套对象

Linq to fill nested objects with pivot

我有如下两个表格

Person{
    int id;
    string name;
}
PersonAddresses{
    int id;
    string AddressType;
    string City;
    string State;
}

这里AddressType指定"BusinessAddress","HomeAddress"等。 我想得到如下结果。

class PersonData{
    int id;
    string Name;
    Address BusinessAddress;
    Address HomeAddress;
    ...
}
class Address{
    string City;
    string State;
}

任何人都可以告诉我如何使用 LINQ 执行此操作而无需多选吗?

我能想出的最简单的查询是这样的

// by doing groupby and select you choose only one address of the type,
// if a person has many.
// If you can be sure that each person has only one address of each type
// then you can simplify these queries a little bit.
IQueryable<PersonAddresses> homeAddresses = from address in addresses
                                            where address.AddressType == "HomeAddress"
                                            group address by address.id into g
                                            select g.First();

IQueryable<PersonAddresses> businessAddresses = from address in addresses
                                                where address.AddressType == "BusinessAddress"
                                                group address by address.id into g
                                                select g.First();
IQueryable<PersonData> data = from person in persons
                              join tmp1 in homeAddresses on person.id equals tmp1.id into ha
                              join tmp2 in businessAddresses on person.id equals tmp2.id into ba
                              from homeAddress in ha.DefaultIfEmpty()
                              from businessAddress in ba.DefaultIfEmpty()
                              select new PersonData {
                                id = person.id,
                                Name = person.name,
                                HomeAddress = homeAddress == null
                                  ? null
                                  : new Address {
                                    City = homeAddress.City,
                                    State = homeAddress.State
                                  },
                                BusinessAddress = businessAddress == null
                                  ? null
                                  : new Address {
                                    City = businessAddress.City,
                                    State = businessAddress.State
                                  },
                              };

您可以获得这样的结果 sql 查询:

  string sql = ((System.Data.Entity.Core.Objects.ObjectQuery) data).ToTraceString();

你可以在这里和那里进行简化,但它的意义不大,因为你通常不能完全期望你的数据是正确的——有些列表有重复,有些值缺失,所以我的查询涵盖了极端情况。

您正在寻找的是 Group join 结合子选择以从组中获取家庭和公司地址:

var query =
    from person in Persons
    join address in PersonAddresses
    on person.id equals address.id into personData
    select new PersonData()
    {
        id = person.id,
        Name = person.name,
        HomeAddress = (
            from data in personData
            where data.AddressType == "HomeAddress"
            select new Address()
            {
                City = data.City,
                State = data.State
            }).FirstOrDefault(),
        BusinessAddress = (
            from data in personData
            where data.AddressType == "BusinessAddress"
            select new Address()
            {
                City = data.City,
                State = data.State
            }).FirstOrDefault(),
    };

这作为单个 SQL 查询运行:

SELECT 
    [Extent1].[id] AS [id], 
    [Extent1].[name] AS [name], 
    [Limit1].[id] AS [id1], 
    [Limit1].[City] AS [City], 
    [Limit1].[State] AS [State], 
    [Limit2].[id] AS [id2], 
    [Limit2].[City] AS [City1], 
    [Limit2].[State] AS [State1]
    FROM   [dbo].[Person] AS [Extent1]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent2].[id] AS [id], 
        [Extent2].[City] AS [City], 
        [Extent2].[State] AS [State]
        FROM [dbo].[PersonAddresses] AS [Extent2]
        WHERE ([Extent1].[id] = [Extent2].[id]) AND (N'HomeAddress' = [Extent2].[AddressType]) ) AS [Limit1]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent3].[id] AS [id], 
        [Extent3].[City] AS [City], 
        [Extent3].[State] AS [State]
        FROM [dbo].[PersonAddresses] AS [Extent3]
        WHERE ([Extent1].[id] = [Extent3].[id]) AND (N'BusinessAddress' = [Extent3].[AddressType]) ) AS [Limit2]