C# Linq使用轴填充嵌套对象

C# Linq使用轴填充嵌套对象,c#,entity-framework,linq,linq-to-entities,C#,Entity Framework,Linq,Linq To Entities,我有两张桌子如下 Person{ int id; string name; } PersonAddresses{ int id; string AddressType; string City; string State; } 此处AddressType指定“业务地址”、“家庭地址”等。 我想得到如下结果 class PersonData{ int id; string Name; Address BusinessAddr

我有两张桌子如下

Person{
    int id;
    string name;
}
PersonAddresses{
    int id;
    string AddressType;
    string City;
    string State;
}
此处AddressType指定“业务地址”、“家庭地址”等。 我想得到如下结果

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
                                  },
                              };

您可以在这里或那里进行简化,但这没有什么意义,因为您通常无法完全期望数据是正确的-有些列表重复,有些值丢失,因此我的查询涵盖了各种情况。

您要查找的是与子选择相结合,以从组中获取家庭和业务地址:

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]

谢谢@Alex Butenko这是一个好主意,但问题是它将转换为3个sql查询(我有几个子对象,比如address)。有没有办法减少查询的数量?@aditya如果您使用的是实体框架,我认为不会有3个查询。我想我会是1我们一次选择地址,然后另一次的人详细信息,对吗?如何查看生成的查询?@aditya请查看我的编辑。IQueryable不会立即从数据库查询任何内容。它只是一个简单的表达式,直到你开始列举它。您不需要考虑将每个语句内联在一起,实体框架将为您完成这项工作。您使用的是哪种linq?实体框架?LINQtoSQL?我正在使用LINQtoEntityFrameworkPerfect。早些时候我尝试过这个,但我错过的只是选择“进入”。谢谢@Ňuf与我的答案的真正区别是什么?@AlexButenko:我的代码只进行一次分组(而不是两次分组和两次连接),这应该在使用LINQ to对象时表现更好。使用entity framework,您的代码生成SQL查询,其中包含两个额外的(而且是多余的)左连接(虽然SQL server可能会对这些进行优化,但我没有尝试)。我的代码似乎也要短得多。
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]