Search code examples
entity-framework-corecross-apply

How do I create a T-SQL Cross-Apply with Entity Framework Core?


I want to create a query with Entity Framework Core which has multiple INNER JOINs and one CROSS APPLY.

I can create the INNER JOINs just fine but can't seem to make the CROSS APPLY work.

  • Did some Google searches bus nothing really useful came up
  • Tried different thing with the Entity Framework linq statements but so far to no avail.

This is the EF query I got so far:

comCommunicationContext
    .Communication
    .AsNoTracking()
    .Where(c => c.Label.InternalName == labelName && 
                c.Businesstransaction.DocumentType.DocumentTypeName == documentType && 
                c.Businesstransaction.SourceSystem.SourceSystemName == sourceSystem)
    .Join(comCommunicationContext.CommunicationOutputChannel,  communicationEntity => communicationEntity.CommunicationId, communicationOutputChannelEntity => communicationOutputChannelEntity.CommunicationId, (communicationEntity, communicationOutputChannelEntity) => new {Communication = communicationEntity, CommunicationOutputChannel = communicationOutputChannelEntity})
    .Where(y => y.CommunicationOutputChannel.OutputChannel == outputChannel)
    .Join(comCommunicationContext.CommunicationStatusOutputChannel.
Where(x => x.Status=="to process"), communicationOutputChannelEntity => communicationOutputChannelEntity.CommunicationOutputChannel.CommunicationOutputChannelId, communicationStatusOutputChannelEntity => communicationStatusOutputChannelEntity.CommunicationOutputChannelId, (outer, inner) => new Communication() { CommunicationDataEnriched = outer.Communication.CommunicationDataEnriched })

Which produces the following T-SQL query:

SELECT [c].[CommunicationDataEnriched]
FROM [Communications] AS [c]
INNER JOIN [Businesstransactions] AS [c.Businesstransaction] ON [c].[BusinessTransactionId] = [c.Businesstransaction].[BusinessTransactionId]
INNER JOIN [DocumentTypes] AS [c.Businesstransaction.DocumentType] ON [c.Businesstransaction].[DocumentTypeId] = [c.Businesstransaction.DocumentType].[DocumentTypeId]
INNER JOIN [SourceSystems] AS [c.Businesstransaction.SourceSystem] ON [c.Businesstransaction].[SourceSystemId] = [c.Businesstransaction.SourceSystem].[SourceSystemId]
INNER JOIN [Labels] AS [c.Label] ON [c].[LabelId] = [c.Label].[LabelId]
INNER JOIN [CommunicationOutputChannels] AS [CommunicationOutputChannels] ON [c].[CommunicationId] = [CommunicationOutputChannels].[CommunicationId]
INNER JOIN (
    SELECT [x].*
    FROM [CommunicationStatusOutputChannels] AS [x]
    WHERE [x].[Status] = 'to process'
) AS [t] ON [CommunicationOutputChannels].[CommunicationOutputChannelId] = [t].[CommunicationOutputChannelId]
WHERE ((([c.Label].[InternalName] = 'labelname') AND ([c.Businesstransaction.DocumentType].[DocumentTypeName] = 'documenttype')) AND ([c.Businesstransaction.SourceSystem].[SourceSystemName] = 'sourcessysem')) AND ([CommunicationOutputChannels].[OutputChannel] = 'email')

What I would like to send to the database is this:

SELECT [c].[CommunicationDataEnriched]
FROM [Communications] AS [c]
INNER JOIN [Businesstransactions] AS [c.Businesstransaction] ON [c].[BusinessTransactionId] = [c.Businesstransaction].[BusinessTransactionId]
INNER JOIN [DocumentTypes] AS [c.Businesstransaction.DocumentType] ON [c.Businesstransaction].[DocumentTypeId] = [c.Businesstransaction.DocumentType].[DocumentTypeId]
INNER JOIN [SourceSystems] AS [c.Businesstransaction.SourceSystem] ON [c.Businesstransaction].[SourceSystemId] = [c.Businesstransaction.SourceSystem].[SourceSystemId]
INNER JOIN [Labels] AS [c.Label] ON [c].[LabelId] = [c.Label].[LabelId]
INNER JOIN [CommunicationOutputChannels] AS [CommunicationOutputChannels] ON [c].[CommunicationId] = [CommunicationOutputChannels].[CommunicationId]
CROSS APPLY (
    SELECT TOP 1 [x].*
    FROM [CommunicationStatusOutputChannels] AS [x]
    WHERE [x].[Status] = 'to process'
    AND [x].[CommunicationOutputChannelId] = [CommunicationOutputChannels].[CommunicationOutputChannelId]
    ORDER BY [x].createdOn DESC
) [t]
WHERE ((([c.Label].[InternalName] = 'labelname') AND ([c.Businesstransaction.DocumentType].[DocumentTypeName] = 'documenttype')) AND ([c.Businesstransaction.SourceSystem].[SourceSystemName] = 'sourcessysem')) AND ([CommunicationOutputChannels].[OutputChannel] = 'email')

The object CommunicationOutputChannel has a navigation property CommunicationStatusOutputChannels of type ICollection<CommunicationStatusOutputChannel> (there's a one-to-many relationn between CommunicationOutputChannel and CommunicationStatusOutputChannels entities).

So the last INNER JOIN between tables CommunicationOutputChannels and CommunicationStatusOutputChannels should be a CROSS APPLY.


Solution

  • Nevermind. Found a solution that works for me.

     var q = from c in comCommunicationContext.Communication.Where(c=>c.Label.InternalName==labelName && c.Businesstransaction.DocumentType.DocumentTypeName==documentType && c.Businesstransaction.SourceSystem.SourceSystemName==sourceSystem)
                        join coc in comCommunicationContext.CommunicationOutputChannel
                        on c.CommunicationId equals coc.CommunicationId
                        from csoc in comCommunicationContext.CommunicationStatusOutputChannel.Where(x=>x.CommunicationOutputChannelId==coc.CommunicationOutputChannelId).OrderByDescending(y=>y.CreatedOn).Take(1)
                        where csoc.Status == "to process"
                        select new Communication(){CommunicationDataEnriched = c.CommunicationDataEnriched}
                    ;