Linq:使用三个嵌套级别进行查询

时间:2012-01-28 09:38:22

标签: c# tsql linq-to-sql c#-4.0

所以我有三张桌子:

CREATE TABLE tblUser
(
    [pkUserID] [int] IDENTITY(1,1) NOT NULL,
    [userName] [varchar](150) NULL,
    [fkCompanyID] [int] NOT NULL
)

CREATE TABLE tblCompany
(
    [pkCompanyID] [int] IDENTITY(1,1) NOT NULL,
    [name] [varchar](255) NULL
)

CREATE TABLE tblSystem
(
    [pkSystemID] [int] IDENTITY(1,1) NOT NULL,
    [systemName] [varchar](150) NULL,
    [fkCompanyID] [int] NULL
)

这些是我的数据传输对象:

public class SystemDTO
{
    public int pkSystemId { get; set; }
    public string Name { get; set; }
    public int? fkCompanyId { get; set; }
}

public class CompanyDTO
{
    public int pkCompanyId { get; set; }
    public string Name { get; set; }
    public IEnumerable<SystemDTO> Systems { get; set; }
}

public class UserDTO
{
    public int pkUserId { get; set; }
    public string Name { get; set; }
    public IEnumerable<CompanyDTO> Companies { get; set; }
}

这是我想要做的Linq查询:

var result= (
        from user in db.tblUsers
        select new UserDTO()
        {
            pkUserId=user.pkUserID,
            Name=user.realName,
            Companies=
                (
                    from company in db.tblCompanies
                    where user.fkCompanyID==company.pkCompanyID
                    select new CompanyDTO()
                    {
                        pkCompanyId=company.pkCompanyID,
                        Name=company.name,
                        Systems=
                        (
                            from system in db.tblSystem
                            where system.fkCompanyId==company.pkCompanyId
                            select new SystemDTO()
                            {
                                pkSystemId=system.pkSystemID,
                                Name=system.systemName,
                                fkCompanyId=system.fkCompanyID
                            }
                        )
                    }
                )
        }
    ).ToList();

此查询的问题在于最内部的查询

from system in db.tblSystem
where system.fkCompanyId==company.pkCompanyId
select new SystemDTO()
{
    pkSystemId=system.pkSystemID,
    Name=system.systemName,
    fkCompanyId=system.fkCompanyID
}

导致linq将sql转换为每个实体一个选择。我知道我可以跳过select并循环结果并设置属性。像这样:

var lsSystem= db.tblSystem.Select (s =>new SystemDTO(){pkSystemId=s.pkSystemID,Name=s.systemName,fkCompanyId=s.fkCompanyID}).ToList();
foreach (var user in result)
    {
        foreach (var company in user.Companies)
        {
            company.Systems=lsSystem.Where (a =>a.fkCompanyId==company.pkCompanyId).ToList();
        }   
    }

这将导致linq执行两个select而不是每个实体一个。所以现在回答我的问题。还有其他方法吗?我可以用另一种方式填充内部集合吗?

任何建议都将不胜感激

修改
建议是使用loadoption。我找不到系统和公司之间的负载选项。但我可以包括之间的loadoption。公司和用户是这样的:

var option=new DataLoadOptions();
option.LoadWith<tblCompany>(a=>a.fkCompanytblUsers);
db.LoadOptions=option;

但是这对查询没有影响,它仍然被翻译成许多选择

EDIT2

正如在答案评论中所述,加载选项不适用于此类linq查询。

3 个答案:

答案 0 :(得分:3)

好的,这是一个提议,您可以使用它来获取单个查询中的所有内容。我将简化数据模型以用于演示目的:

select *
from ParentTable
join ChildLevel1 on ...
join ChildLevel2 on ...

该查询将同时为您提供所有三个树级别。这将是非常有效的。但数据将是多余的。您需要进行一些客户端处理才能使其再次可用:

var parents = from x in queryResults
group x by new { /* all parent columns here */ }) into g
select new Parent()
{
 ParentData = g.Key,
 Children1 = from x in g
             group x by new { /* all ChildLevel1 columns here */ }) into g
             select new Child1()
             {
              Child1Data = g.Key,
              Children2 = ... //repeat
             }
}

您需要通过分组来删除冗余。换句话说:查询已对数据进行非规范化,我们需要再次对其进行规范化。

这种方法非常麻烦但速度很快。

答案 1 :(得分:1)

我自己弄清楚了。我能看到的最好的方法是这样做(但如果其他人有更好的建议请再加上):

var lsSystem= db.tblSystem.Select (s =>new SystemDTO()
                                        {
                                            pkSystemId=s.pkSystemID,
                                            Name=s.systemName,
                                            fkCompanyId=s.fkCompanyID
                                        }
                                ).ToLookup (s =>s.fkCompanyId);

然后在linq查询中使用lsSystem,如下所示:

var result= (
        from user in db.tblUsers
        select new UserDTO()
        {
            pkUserId=user.pkUserID,
            Name=user.realName,
            Companies=
                (
                    from company in db.tblCompanies
                    where user.fkCompanyID==company.pkCompanyID
                    select new CompanyDTO()
                    {
                        pkCompanyId=company.pkCompanyID,
                        Name=company.name,
                        Systems=lsSystem[company.pkCompanyID]
                    }
                )
        }
    ).ToList();

这将导致两个选择语句一个用于系统,一个用于公司用户

答案 2 :(得分:0)

您是否查看了LoadOptions以及更具体的LoadWith

这将阻止Linq2sql延迟加载,并将进行急切加载。

这里的简单示例: http://davidhayden.com/blog/dave/archive/2007/08/05/LINQToSQLLazyLoadingPropertiesSpecifyingPreFetchWhenNeededPerformance.aspx