所以我有三张桌子:
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查询。
答案 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延迟加载,并将进行急切加载。