Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

March 18, 2015 / by Zsolt Soczó

EF gyerek kollekció rendezés

Néha szeretnénk nem csak egy entitás listát, hanem annak gyerekeit is rendeztetni, azaz az egy szülő alá tartozó gyerekeket order by-olni.

Egy lehetséges megoldás:

using (var c = new EdbContext())
{
    var jobs = c.LoaderJobs
        .Include("LoaderJobSteps")
        .Include("LoaderJobSteps.ExtractPathLocation")
        .Include("LoaderJobSteps.FormatFilePathLocation")
        .OrderBy(j => j.ExecutionOrder).ToList();

    //A bit complicated to be able to order LoaderJobSteps properly
    var x = jobs.Select(job => new { J = job, JS = job.LoaderJobSteps.OrderBy(js => js.ExecutionOrder).ToList() });

    return x.Select(f => f.J).ToList();
}

Szándékaim szerint a gyerekkollekciók order by-át is az adatbázissal végeztettem volna, de jól látható a kódból, hogy itt .netből történik a gyerekek (LoaderJobSteps) rendezése.
Ha kiveszem az első ToList()-et, akkor az EF helyesen áttolja a 2. order by-t is az SQL Serverre, de akkor meg nem tölti be az unokákat (LoaderJobSteps.ExtractPathLocation).
Ebben a példában nincs jelentősége hol rendezek, de ha valaki tudja, mitől nem megy ilyenkor az Include, érdekelne a megoldás.

Ha benn van az első ToList(), akkor az SQL ok, benne van minden eagerly loaded entitás, de nincs benne order by a gyerekekre, az a LINQ2Objects fogja végrehajtani. (Extent2-re nincs order by).

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Description] AS [Description], 
    [Project1].[ExecutionOrder] AS [ExecutionOrder], 
    [Project1].[C1] AS [C1], 
    [Project1].[Id1] AS [Id1], 
    [Project1].[StepName] AS [StepName], 
    [Project1].[ExecutionOrder1] AS [ExecutionOrder1], 
    [Project1].[ProcedureNamePrepare] AS [ProcedureNamePrepare], 
    [Project1].[ProcedureNameImport] AS [ProcedureNameImport], 
    [Project1].[ProcedureNameLoad] AS [ProcedureNameLoad], 
    [Project1].[ExtractFileName] AS [ExtractFileName], 
    [Project1].[FailOnMissingFile] AS [FailOnMissingFile], 
    [Project1].[FormatFileName] AS [FormatFileName], 
    [Project1].[Id2] AS [Id2], 
    [Project1].[FolderPath] AS [FolderPath], 
    [Project1].[Id3] AS [Id3], 
    [Project1].[FolderPath1] AS [FolderPath1], 
    [Project1].[LoaderJobId] AS [LoaderJobId]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Description] AS [Description], 
        [Extent1].[ExecutionOrder] AS [ExecutionOrder], 
        [Join2].[Id1] AS [Id1], 
        [Join2].[StepName] AS [StepName], 
        [Join2].[ExecutionOrder] AS [ExecutionOrder1], 
        [Join2].[ProcedureNamePrepare] AS [ProcedureNamePrepare], 
        [Join2].[ProcedureNameImport] AS [ProcedureNameImport], 
        [Join2].[ProcedureNameLoad] AS [ProcedureNameLoad], 
        [Join2].[ExtractFileName] AS [ExtractFileName], 
        [Join2].[FailOnMissingFile] AS [FailOnMissingFile], 
        [Join2].[FormatFileName] AS [FormatFileName], 
        [Join2].[LoaderJobId] AS [LoaderJobId], 
        [Join2].[Id2] AS [Id2], 
        [Join2].[FolderPath1] AS [FolderPath], 
        [Join2].[Id3] AS [Id3], 
        [Join2].[FolderPath2] AS [FolderPath1], 
        CASE WHEN ([Join2].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[LoaderJob] AS [Extent1]
        LEFT OUTER JOIN  (SELECT [Extent2].[Id] AS [Id1], [Extent2].[StepName] AS [StepName], [Extent2].[ExecutionOrder] AS [ExecutionOrder], [Extent2].[ProcedureNamePrepare] AS [ProcedureNamePrepare], [Extent2].[ProcedureNameImport] AS [ProcedureNameImport], [Extent2].[ProcedureNameLoad] AS [ProcedureNameLoad], [Extent2].[ExtractFileName] AS [ExtractFileName], [Extent2].[FailOnMissingFile] AS [FailOnMissingFile], [Extent2].[FormatFileName] AS [FormatFileName], [Extent2].[LoaderJobId] AS [LoaderJobId], [Extent3].[Id] AS [Id2], [Extent3].[FolderPath] AS [FolderPath1], [Extent4].[Id] AS [Id3], [Extent4].[FolderPath] AS [FolderPath2]
            FROM   [dbo].[LoaderJobStep] AS [Extent2]
            LEFT OUTER JOIN [dbo].[PathLocation] AS [Extent3] ON [Extent2].[ExtractPathLocationId] = [Extent3].[Id]
            LEFT OUTER JOIN [dbo].[PathLocation] AS [Extent4] ON [Extent2].[FormatFilePathLocationId] = [Extent4].[Id] ) AS [Join2] ON [Extent1].[Id] = [Join2].[LoaderJobId]
    )  AS [Project1]
    ORDER BY [Project1].[ExecutionOrder] ASC, [Project1].[Id] ASC, [Project1].[C1] ASC

Az első ToList() nélkül:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Description] AS [Description], 
    [Extent1].[ExecutionOrder] AS [ExecutionOrder]
    FROM [dbo].[LoaderJob] AS [Extent1]
    ORDER BY [Extent1].[ExecutionOrder] ASC

Hm. Tippek?

Közben rájöttem, megválaszolom magamnak a kérdést. :) A végére kell rakni az include-okat:

using (var c = new EdbContext())
{
var jobs = c.LoaderJobs
.OrderBy(j => j.ExecutionOrder);

//A bit complicated to be able to order LoaderJobSteps properly
var x = jobs.Select(job => new { J = job, JS = job.LoaderJobSteps.OrderBy(js => js.ExecutionOrder) });

return x.Select(f => f.J).Include(“LoaderJobSteps”)
.Include(“LoaderJobSteps.ExtractPathLocation”)
.Include(“LoaderJobSteps.FormatFilePathLocation”).ToList();
}
[/source]

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Description] AS [Description], 
    [Project1].[ExecutionOrder] AS [ExecutionOrder], 
    [Project1].[C1] AS [C1], 
    [Project1].[Id1] AS [Id1], 
    [Project1].[StepName] AS [StepName], 
    [Project1].[ExecutionOrder1] AS [ExecutionOrder1], 
    [Project1].[ProcedureNamePrepare] AS [ProcedureNamePrepare], 
    [Project1].[ProcedureNameImport] AS [ProcedureNameImport], 
    [Project1].[ProcedureNameLoad] AS [ProcedureNameLoad], 
    [Project1].[ExtractFileName] AS [ExtractFileName], 
    [Project1].[FailOnMissingFile] AS [FailOnMissingFile], 
    [Project1].[FormatFileName] AS [FormatFileName], 
    [Project1].[Id2] AS [Id2], 
    [Project1].[FolderPath] AS [FolderPath], 
    [Project1].[Id3] AS [Id3], 
    [Project1].[FolderPath1] AS [FolderPath1], 
    [Project1].[LoaderJobId] AS [LoaderJobId]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Description] AS [Description], 
        [Extent1].[ExecutionOrder] AS [ExecutionOrder], 
        [Join2].[Id1] AS [Id1], 
        [Join2].[StepName] AS [StepName], 
        [Join2].[ExecutionOrder] AS [ExecutionOrder1], 
        [Join2].[ProcedureNamePrepare] AS [ProcedureNamePrepare], 
        [Join2].[ProcedureNameImport] AS [ProcedureNameImport], 
        [Join2].[ProcedureNameLoad] AS [ProcedureNameLoad], 
        [Join2].[ExtractFileName] AS [ExtractFileName], 
        [Join2].[FailOnMissingFile] AS [FailOnMissingFile], 
        [Join2].[FormatFileName] AS [FormatFileName], 
        [Join2].[LoaderJobId] AS [LoaderJobId], 
        [Join2].[Id2] AS [Id2], 
        [Join2].[FolderPath1] AS [FolderPath], 
        [Join2].[Id3] AS [Id3], 
        [Join2].[FolderPath2] AS [FolderPath1], 
        CASE WHEN ([Join2].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[LoaderJob] AS [Extent1]
        LEFT OUTER JOIN  (SELECT [Extent2].[Id] AS [Id1], [Extent2].[StepName] AS [StepName], [Extent2].[ExecutionOrder] AS [ExecutionOrder], [Extent2].[ProcedureNamePrepare] AS [ProcedureNamePrepare], [Extent2].[ProcedureNameImport] AS [ProcedureNameImport], [Extent2].[ProcedureNameLoad] AS [ProcedureNameLoad], [Extent2].[ExtractFileName] AS [ExtractFileName], [Extent2].[FailOnMissingFile] AS [FailOnMissingFile], [Extent2].[FormatFileName] AS [FormatFileName], [Extent2].[LoaderJobId] AS [LoaderJobId], [Extent3].[Id] AS [Id2], [Extent3].[FolderPath] AS [FolderPath1], [Extent4].[Id] AS [Id3], [Extent4].[FolderPath] AS [FolderPath2]
            FROM   [dbo].[LoaderJobStep] AS [Extent2]
            LEFT OUTER JOIN [dbo].[PathLocation] AS [Extent3] ON [Extent2].[ExtractPathLocationId] = [Extent3].[Id]
            LEFT OUTER JOIN [dbo].[PathLocation] AS [Extent4] ON [Extent2].[FormatFilePathLocationId] = [Extent4].[Id] ) AS [Join2] ON [Extent1].[Id] = [Join2].[LoaderJobId]
    )  AS [Project1]
    ORDER BY [Project1].[ExecutionOrder] ASC, [Project1].[Id] ASC, [Project1].[C1] ASC

Hm, mégse jó, nincs benne az sql-ben a 2. order by. Szóval a kérdés nyitott, át lehet tolni adatbázis oldalra a child kollekció order by-t?

Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

LEAVE A COMMENT

1 COMMENTS

  • Peter Kiss March 19, 2015

    Akkor mi történik, ha a child lesz a parent? A lekérdezést a LoaderJobSteps.ExtractPathLocation-ből indítod, ehhez include-olsz mindent, és ezt rendezed egy Orderby-ThenBy kombóval.