score:1

Accepted answer

Try avoiding the

context.Procedures.Where(h => h.Id == x.Key).Select(h => h.Code).FirstOrDefault()

by including the Code in the GroupBy clause - I know it seems redundant, but EF is known to have problems translating operations on grouping that involve something other than using key accessors and aggregates:

//...
.GroupBy(x => new { Id = x.VisitLine.ProcedureId, x.VisitLine.Procedure.Code })
.Select(x => new
{
    Id = x.Key.Id,
    PaidAmount = x.Sum(t => t.PaidAmount),
    Code = x.Key.Code
}).ToArray();

Update: the above generates the following SQL in my test environment (latest EF6.1.3):

SELECT
    1 AS [C1],
    [GroupBy1].[K1] AS [ProcedureId],
    [GroupBy1].[A1] AS [C2],
    [GroupBy1].[K2] AS [Code]
    FROM ( SELECT
        [Extent5].[ProcedureId] AS [K1],
        [Extent6].[Code] AS [K2],
        SUM([Filter1].[PaidAmount]) AS [A1]
        FROM    (SELECT [Extent1].[VisitLineId] AS [VisitLineId], [Extent1].[PaidAmount] AS [PaidAmount]
            FROM    [dbo].[TransactionLine] AS [Extent1]
            INNER JOIN [dbo].[Transaction] AS [Extent2] ON [Extent1].[TransactionId] = [Extent2].[Id]
            LEFT OUTER JOIN [dbo].[Eob] AS [Extent3] ON [Extent2].[EobId] = [Extent3].[Id]
            LEFT OUTER JOIN [dbo].[EobBatch] AS [Extent4] ON [Extent3].[EobBatchId] = [Extent4].[Id]
            WHERE (1433 = [Extent2].[TransactionTypeId]) AND ([Extent3].[EobBatchId] IS NULL OR [Extent4].[Status] = 1) ) AS [Filter1]
        LEFT OUTER JOIN [dbo].[VisitLine] AS [Extent5] ON [Filter1].[VisitLineId] = [Extent5].[Id]
        LEFT OUTER JOIN [dbo].[Procedure] AS [Extent6] ON [Extent5].[ProcedureId] = [Extent6].[Id]
        GROUP BY [Extent5].[ProcedureId], [Extent6].[Code]
    )  AS [GroupBy1]

which is much better as I expected.

Update 2: EF is a strange beast. Using double projection produces the desired result:

//...
.GroupBy(x => x.VisitLine.ProcedureId)
.Select(x => new
{
    Id = x.Key,
    PaidAmount = x.Sum(t => t.PaidAmount),
})
.Select(x => new
{
    x.Id,
    x.PaidAmount,
    Code = context.Procedures.Where(h => h.Id == x.Id).Select(h => h.Code).FirstOrDefault()
}).ToArray();

which produces the following:

SELECT
    1 AS [C1],
    [Project2].[ProcedureId] AS [ProcedureId],
    [Project2].[C1] AS [C2],
    [Project2].[C2] AS [C3]
    FROM ( SELECT
        [GroupBy1].[A1] AS [C1],
        [GroupBy1].[K1] AS [ProcedureId],
        (SELECT TOP (1)
            [Extent6].[Code] AS [Code]
            FROM [dbo].[Procedure] AS [Extent6]
            WHERE [Extent6].[Id] = [GroupBy1].[K1]) AS [C2]
        FROM ( SELECT
            [Extent5].[ProcedureId] AS [K1],
            SUM([Filter1].[PaidAmount]) AS [A1]
            FROM   (SELECT [Extent1].[VisitLineId] AS [VisitLineId], [Extent1].[PaidAmount] AS [PaidAmount]
                FROM    [dbo].[TransactionLine] AS [Extent1]
                INNER JOIN [dbo].[Transaction] AS [Extent2] ON [Extent1].[TransactionId] = [Extent2].[Id]
                LEFT OUTER JOIN [dbo].[Eob] AS [Extent3] ON [Extent2].[EobId] = [Extent3].[Id]
                LEFT OUTER JOIN [dbo].[EobBatch] AS [Extent4] ON [Extent3].[EobBatchId] = [Extent4].[Id]
                WHERE (1433 = [Extent2].[TransactionTypeId]) AND ([Extent3].[EobBatchId] IS NULL OR [Extent4].[Status] = 1) ) AS [Filter1]
            LEFT OUTER JOIN [dbo].[VisitLine] AS [Extent5] ON [Filter1].[VisitLineId] = [Extent5].[Id]
            GROUP BY [Extent5].[ProcedureId]
        )  AS [GroupBy1]
    )  AS [Project2]

P.S. If it's unclear, the answer to your concrete question

Is it possible to force EF to generate SQL exactly as it was written within linq ?

is no. Contrary, you should write LINQ query in a certain way in order to get the desired (or closer) SQL query.


Related Articles