Linq: запрос с тремя вложенными уровнями - PullRequest
4 голосов
/ 28 января 2012

Итак, у меня есть три таблицы:

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 в один выбор для объекта. Я знаю, что могу пропустить выбор и зациклить результат и установить свойство. Как это:

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 делать два выбора, а не по одному на объект. Так что теперь к моим вопросам. Есть ли другой способ сделать это? Могу ли я заполнить внутреннюю коллекцию по-другому?

Любое предложение будет оценено

EDIT
Было предложено использовать loadoption. Я не могу найти вариант загрузки между системой и компанией. Но я могу включить loadoption между. Компания и пользователь, как это:

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

Но это не влияет на запрос, который все еще переводится на многие выборки

EDIT2

Как сказано в комментариях к ответам, параметры загрузки не применяются для этого типа запроса linq.

Ответы [ 3 ]

3 голосов
/ 28 января 2012

Хорошо, вот предложение, которое вы используете, чтобы получить все в одном запросе. Я упрощу модель данных для демонстрационных целей:

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 голос
/ 28 января 2012

Я сам разбираюсь.Лучший способ, который я вижу, - это сделать так (но, если у кого-то есть более подходящие предложения, добавьте их):

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

А затем используйте lsSystem в запросе 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=lsSystem[company.pkCompanyID]
                    }
                )
        }
    ).ToList();

Это приведет к двум операторам выбора, одному для системы и одному для пользователей для компаний

0 голосов
/ 28 января 2012

Вы смотрели на LoadOptions и более конкретно LoadWith.

Это предотвратит отложенную загрузку Linq2sql и сделает загрузку нетерпеливой.

Простой пример здесь: http://davidhayden.com/blog/dave/archive/2007/08/05/LINQToSQLLazyLoadingPropertiesSpecifyingPreFetchWhenNeededPerformance.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...