Entity Framework 6 too many joins - c#

I have the following LINQ query:
var query = from a in c.ArticleSet
where a.GlobalAccess == false &&
a.Published == true &&
a.MagazineSet.IsPublished == true &&
a.MagazineSet.Press_Id == pressId &&
a.Tests.Id==a.Id &&
a.Tests.IsDeleted == false &&
a.Tests.IsPublished == true
orderby a.Id descending
select a.Id;
It tranforms to SQL like this:
ADO.NET:Execute Reader "SELECT
[Project1].[Id] AS [Id]
FROM ( SELECT
[Filter3].[Id1] AS [Id]
FROM (SELECT [Filter2].[Id1], [Filter2].[Magazine_Id1], [Filter2].[Press_Id1], [Filter2].[Press_Id2], [Filter2].[Press_Id3]
FROM (SELECT [Filter1].[Id1], [Filter1].[Magazine_Id1], [Extent5].[Press_Id] AS [Press_Id1], [Extent6].[Press_Id] AS [Press_Id2], [Extent7].[Press_Id] AS [Press_Id3]
FROM (SELECT [Extent1].[Id] AS [Id1], [Extent1].[Magazine_Id] AS [Magazine_Id1]
FROM [dbo].[ArticleSet] AS [Extent1]
INNER JOIN [dbo].[Tests] AS [Extent2] ON ([Extent1].[Id] = [Extent2].[Id]) AND ([Extent2].[Id] = [Extent1].[Id])
INNER JOIN [dbo].[MagazineSet] AS [Extent3] ON [Extent1].[Magazine_Id] = [Extent3].[Id]
WHERE (0 = [Extent1].[GlobalAccess]) AND (1 = [Extent1].[Published]) AND (1 = [Extent3].[IsPublished]) ) AS [Filter1]
INNER JOIN [dbo].[MagazineSet] AS [Extent4] ON [Filter1].[Magazine_Id1] = [Extent4].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent5] ON [Filter1].[Magazine_Id1] = [Extent5].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent6] ON [Filter1].[Magazine_Id1] = [Extent6].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent7] ON [Filter1].[Magazine_Id1] = [Extent7].[Id]
INNER JOIN [dbo].[Tests] AS [Extent8] ON [Filter1].[Id1] = [Extent8].[Id]
WHERE 0 = [Extent8].[IsDeleted] ) AS [Filter2]
INNER JOIN [dbo].[Tests] AS [Extent9] ON [Filter2].[Id1] = [Extent9].[Id]
INNER JOIN [dbo].[Tests] AS [Extent10] ON [Filter2].[Id1] = [Extent10].[Id]
WHERE 1 = [Extent10].[IsPublished] ) AS [Filter3]
INNER JOIN [dbo].[Tests] AS [Extent11] ON [Filter3].[Id1] = [Extent11].[Id]
WHERE (([Filter3].[Press_Id1] = #p__linq__0) AND ( NOT ([Filter3].[Press_Id2] IS NULL OR #p__linq__0 IS NULL))) OR (([Filter3].[Press_Id3] IS NULL) AND (#p__linq__0 IS NULL))
) AS [Project1]
ORDER BY [Project1].[Id] DESC"
The command text
"SELECT [Project1].[Id] AS [Id]
FROM ( SELECT
[Filter3].[Id1] AS [Id]
FROM (SELECT [Filter2].[Id1], [Filter2].[Magazine_Id1], [Filter2].[Press_Id1], [Filter2].[Press_Id2], [Filter2].[Press_Id3]
FROM (SELECT [Filter1].[Id1], [Filter1].[Magazine_Id1], [Extent5].[Press_Id] AS [Press_Id1], [Extent6].[Press_Id] AS [Press_Id2], [Extent7].[Press_Id] AS [Press_Id3]
FROM (SELECT [Extent1].[Id] AS [Id1], [Extent1].[Magazine_Id] AS [Magazine_Id1]
FROM [dbo].[ArticleSet] AS [Extent1]
INNER JOIN [dbo].[Tests] AS [Extent2] ON ([Extent1].[Id] = [Extent2].[Id]) AND ([Extent2].[Id] = [Extent1].[Id])
INNER JOIN [dbo].[MagazineSet] AS [Extent3] ON [Extent1].[Magazine_Id] = [Extent3].[Id]
WHERE (0 = [Extent1].[GlobalAccess]) AND (1 = [Extent1].[Published]) AND (1 = [Extent3].[IsPublished]) ) AS [Filter1]
INNER JOIN [dbo].[MagazineSet] AS [Extent4] ON [Filter1].[Magazine_Id1] = [Extent4].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent5] ON [Filter1].[Magazine_Id1] = [Extent5].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent6] ON [Filter1].[Magazine_Id1] = [Extent6].[Id]
LEFT OUTER JOIN [dbo].[MagazineSet] AS [Extent7] ON [Filter1].[Magazine_Id1] = [Extent7].[Id]
INNER JOIN [dbo].[Tests] AS [Extent8] ON [Filter1].[Id1] = [Extent8].[Id]
WHERE 0 = [Extent8].[IsDeleted] ) AS [Filter2]
INNER JOIN [dbo].[Tests] AS [Extent9] ON [Filter2].[Id1] = [Extent9].[Id]
INNER JOIN [dbo].[Tests] AS [Extent10] ON [Filter2].[Id1] = [Extent10].[Id]
WHERE 1 = [Extent10].[IsPublished] ) AS [Filter3]
INNER JOIN [dbo].[Tests] AS [Extent11] ON [Filter3].[Id1] = [Extent11].[Id]
WHERE (([Filter3].[Press_Id1] = #p__linq__0) AND ( NOT ([Filter3].[Press_Id2] IS NULL OR #p__linq__0 IS NULL))) OR (([Filter3].[Press_Id3] IS NULL) AND (#p__linq__0 IS NULL))
) AS [Project1]
ORDER BY [Project1].[Id] DESC
I think 3 join is enough in this example.
Why does Entity framework create such huge query?

Your linq query is fine, other than the fact that it references a lot of tables at once. If this is really what you want, there there is nothing to be afraid of. It's only not "super efficient" because you are referencing 3 tables at once.
Just remember that the sql query that EF produces is machine-created. It's not always the best, but it should alway be perfectly valid. I'm not sure why it chose this form over another, but if it works, why worry?
I always suggest that people limit their queries to only the referencing tables they need. As the system grows, the processing time will increase as well, and with this many table joins, it might start to get more noticeable.

Related

EF Core difficulty with INNER JOINS and LEFT JOINS

I am trying to retrieve some data from a very big database. In order to retrieve it I need to get some data from within 6 inner joins. The following options is what I tried already:
Option 1 Using EF Core en ThenInclude
var enquiry = await _context.Enquiries
.Where(e => e.Id == id)
.Include(e => e.EnqPartJobs.Where(ej => ej.RecordStateId == 0))
.ThenInclude(ej => ej.AnswerGrpBatch)
.ThenInclude(agb => agb.AnswerGrps)
.ThenInclude(ag => ag.EnqPropResults)
.ThenInclude(epr => epr.EnqPropResultDevs)
.ThenInclude(epr => epr.Deviation)
.ToListAsync();
The advantage of this method is that it will map nicely in a Enquiry class with a list of AnswerGrpBatches. Btw I did not name these classes this is just how the database looks.
When looking at the query it is the following:
SELECT [e].[SID], [e].[FK_CRMPartner_SID], [e].[FK_Certificate_SID], [e].[createDate], [e].[displayDate], [e].[extID], [e].[finishDate], [e].[fixedGenId], [e].[aName], [t2].[SID], [t2].[FK_Enquiry_SID], [t2].[aName], [t2].[FK_RecordState_SID], [t2].[remark], [t2].[validFrom], [t2].[validTo], [t2].[SID0], [t2].[FK_EnqPartJob_SID], [t2].[FK_RecordState_SID0], [t2].[SID1], [t2].[FK_AnswerGrpBatch_SID], [t2].[FK_RecordState_SID1], [t2].[SID00], [t2].[FK_AnswerGrp_SID], [t2].[FK_EnqPropertyForm_SID], [t2].[FK_PossResult_SID], [t2].[FK_RecordState_SID00], [t2].[value], [t2].[SID000], [t2].[FK_Deviation_SID], [t2].[FK_EnqPropResult_SID], [t2].[SID0000], [t2].[aDescription], [t2].[aName0]
FROM [Enquiry] AS [e]
LEFT JOIN (
SELECT [e0].[SID], [e0].[FK_Enquiry_SID], [e0].[aName], [e0].[FK_RecordState_SID], [e0].[remark], [e0].[validFrom], [e0].[validTo], [a].[SID] AS [SID0], [a].[FK_EnqPartJob_SID], [a].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t1].[SID] AS [SID1], [t1].[FK_AnswerGrpBatch_SID], [t1].[FK_RecordState_SID] AS [FK_RecordState_SID1], [t1].[SID0] AS [SID00], [t1].[FK_AnswerGrp_SID], [t1].[FK_EnqPropertyForm_SID], [t1].[FK_PossResult_SID], [t1].[FK_RecordState_SID0] AS [FK_RecordState_SID00], [t1].[value], [t1].[SID00] AS [SID000], [t1].[FK_Deviation_SID], [t1].[FK_EnqPropResult_SID], [t1].[SID000] AS [SID0000], [t1].[aDescription], [t1].[aName] AS [aName0]
FROM [EnqPartJob] AS [e0]
LEFT JOIN [AnswerGrpBatch] AS [a] ON [e0].[SID] = [a].[FK_EnqPartJob_SID]
LEFT JOIN (
SELECT [a0].[SID], [a0].[FK_AnswerGrpBatch_SID], [a0].[FK_RecordState_SID], [t0].[SID] AS [SID0], [t0].[FK_AnswerGrp_SID], [t0].[FK_EnqPropertyForm_SID], [t0].[FK_PossResult_SID], [t0].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t0].[value], [t0].[SID0] AS [SID00], [t0].[FK_Deviation_SID], [t0].[FK_EnqPropResult_SID], [t0].[SID00] AS [SID000], [t0].[aDescription], [t0].[aName]
FROM [AnswerGrp] AS [a0]
LEFT JOIN (
SELECT [e1].[SID], [e1].[FK_AnswerGrp_SID], [e1].[FK_EnqPropertyForm_SID], [e1].[FK_PossResult_SID], [e1].[FK_RecordState_SID], [e1].[value], [t].[SID] AS [SID0], [t].[FK_Deviation_SID], [t].[FK_EnqPropResult_SID], [t].[SID0] AS [SID00], [t].[aDescription], [t].[aName]
FROM [EnqPropResult] AS [e1]
LEFT JOIN (
SELECT [e2].[SID], [e2].[FK_Deviation_SID], [e2].[FK_EnqPropResult_SID], [d].[SID] AS [SID0], [d].[aDescription], [d].[aName]
FROM [EnqPropResultDev] AS [e2]
INNER JOIN [Deviation] AS [d] ON [e2].[FK_Deviation_SID] = [d].[SID]
) AS [t] ON [e1].[SID] = [t].[FK_EnqPropResult_SID]
) AS [t0] ON [a0].[SID] = [t0].[FK_AnswerGrp_SID]
) AS [t1] ON [a].[SID] = [t1].[FK_AnswerGrpBatch_SID]
WHERE [e0].[FK_RecordState_SID] = 0
) AS [t2] ON [e].[SID] = [t2].[FK_Enquiry_SID]
WHERE [e].[SID] = 1790797
But it needs to be one of these 2 options:
using a IS NOT NULL statement but I cannot figure out how to make this one beceause it is deep inside different classes.
SELECT [e].[SID], [e].[FK_CRMPartner_SID], [e].[FK_Certificate_SID], [e].[createDate], [e].[displayDate], [e].[extID], [e].[finishDate], [e].[fixedGenId], [e].[aName], [t2].[SID], [t2].[FK_Enquiry_SID], [t2].[aName], [t2].[FK_RecordState_SID], [t2].[remark], [t2].[validFrom], [t2].[validTo], [t2].[SID0], [t2].[FK_EnqPartJob_SID], [t2].[FK_RecordState_SID0], [t2].[SID1], [t2].[FK_AnswerGrpBatch_SID], [t2].[FK_RecordState_SID1], [t2].[SID00], [t2].[FK_AnswerGrp_SID], [t2].[FK_EnqPropertyForm_SID], [t2].[FK_PossResult_SID], [t2].[FK_RecordState_SID00], [t2].[value], [t2].[SID000], [t2].[FK_Deviation_SID], [t2].[FK_EnqPropResult_SID], [t2].[SID0000], [t2].[aDescription], [t2].[aName0]
FROM [Enquiry] AS [e]
LEFT JOIN (
SELECT [e0].[SID], [e0].[FK_Enquiry_SID], [e0].[aName], [e0].[FK_RecordState_SID], [e0].[remark], [e0].[validFrom], [e0].[validTo], [a].[SID] AS [SID0], [a].[FK_EnqPartJob_SID], [a].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t1].[SID] AS [SID1], [t1].[FK_AnswerGrpBatch_SID], [t1].[FK_RecordState_SID] AS [FK_RecordState_SID1], [t1].[SID0] AS [SID00], [t1].[FK_AnswerGrp_SID], [t1].[FK_EnqPropertyForm_SID], [t1].[FK_PossResult_SID], [t1].[FK_RecordState_SID0] AS [FK_RecordState_SID00], [t1].[value], [t1].[SID00] AS [SID000], [t1].[FK_Deviation_SID], [t1].[FK_EnqPropResult_SID], [t1].[SID000] AS [SID0000], [t1].[aDescription], [t1].[aName] AS [aName0]
FROM [EnqPartJob] AS [e0]
LEFT JOIN [AnswerGrpBatch] AS [a] ON [e0].[SID] = [a].[FK_EnqPartJob_SID]
LEFT JOIN (
SELECT [a0].[SID], [a0].[FK_AnswerGrpBatch_SID], [a0].[FK_RecordState_SID], [t0].[SID] AS [SID0], [t0].[FK_AnswerGrp_SID], [t0].[FK_EnqPropertyForm_SID], [t0].[FK_PossResult_SID], [t0].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t0].[value], [t0].[SID0] AS [SID00], [t0].[FK_Deviation_SID], [t0].[FK_EnqPropResult_SID], [t0].[SID00] AS [SID000], [t0].[aDescription], [t0].[aName]
FROM [AnswerGrp] AS [a0]
LEFT JOIN (
SELECT [e1].[SID], [e1].[FK_AnswerGrp_SID], [e1].[FK_EnqPropertyForm_SID], [e1].[FK_PossResult_SID], [e1].[FK_RecordState_SID], [e1].[value], [t].[SID] AS [SID0], [t].[FK_Deviation_SID], [t].[FK_EnqPropResult_SID], [t].[SID0] AS [SID00], [t].[aDescription], [t].[aName]
FROM [EnqPropResult] AS [e1]
LEFT JOIN (
SELECT [e2].[SID], [e2].[FK_Deviation_SID], [e2].[FK_EnqPropResult_SID], [d].[SID] AS [SID0], [d].[aDescription], [d].[aName]
FROM [EnqPropResultDev] AS [e2]
INNER JOIN [Deviation] AS [d] ON [e2].[FK_Deviation_SID] = [d].[SID]
) AS [t] ON [e1].[SID] = [t].[FK_EnqPropResult_SID]
) AS [t0] ON [a0].[SID] = [t0].[FK_AnswerGrp_SID]
) AS [t1] ON [a].[SID] = [t1].[FK_AnswerGrpBatch_SID]
WHERE [e0].[FK_RecordState_SID] = 0
) AS [t2] ON [e].[SID] = [t2].[FK_Enquiry_SID]
WHERE [e].[SID] = 1790797 AND [t2].[SID000] IS NOT NULL
Forcing INNER JOINS:
SELECT [e].[SID], [e].[FK_CRMPartner_SID], [e].[FK_Certificate_SID], [e].[createDate], [e].[displayDate], [e].[extID], [e].[finishDate], [e].[fixedGenId], [e].[aName], [t2].[SID], [t2].[FK_Enquiry_SID], [t2].[aName], [t2].[FK_RecordState_SID], [t2].[remark], [t2].[validFrom], [t2].[validTo], [t2].[SID0], [t2].[FK_EnqPartJob_SID], [t2].[FK_RecordState_SID0], [t2].[SID1], [t2].[FK_AnswerGrpBatch_SID], [t2].[FK_RecordState_SID1], [t2].[SID00], [t2].[FK_AnswerGrp_SID], [t2].[FK_EnqPropertyForm_SID], [t2].[FK_PossResult_SID], [t2].[FK_RecordState_SID00], [t2].[value], [t2].[SID000], [t2].[FK_Deviation_SID], [t2].[FK_EnqPropResult_SID], [t2].[SID0000], [t2].[aDescription], [t2].[aName0]
FROM [Enquiry] AS [e]
LEFT JOIN (
SELECT [e0].[SID], [e0].[FK_Enquiry_SID], [e0].[aName], [e0].[FK_RecordState_SID], [e0].[remark], [e0].[validFrom], [e0].[validTo], [a].[SID] AS [SID0], [a].[FK_EnqPartJob_SID], [a].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t1].[SID] AS [SID1], [t1].[FK_AnswerGrpBatch_SID], [t1].[FK_RecordState_SID] AS [FK_RecordState_SID1], [t1].[SID0] AS [SID00], [t1].[FK_AnswerGrp_SID], [t1].[FK_EnqPropertyForm_SID], [t1].[FK_PossResult_SID], [t1].[FK_RecordState_SID0] AS [FK_RecordState_SID00], [t1].[value], [t1].[SID00] AS [SID000], [t1].[FK_Deviation_SID], [t1].[FK_EnqPropResult_SID], [t1].[SID000] AS [SID0000], [t1].[aDescription], [t1].[aName] AS [aName0]
FROM [EnqPartJob] AS [e0]
LEFT JOIN [AnswerGrpBatch] AS [a] ON [e0].[SID] = [a].[FK_EnqPartJob_SID]
LEFT JOIN (
SELECT [a0].[SID], [a0].[FK_AnswerGrpBatch_SID], [a0].[FK_RecordState_SID], [t0].[SID] AS [SID0], [t0].[FK_AnswerGrp_SID], [t0].[FK_EnqPropertyForm_SID], [t0].[FK_PossResult_SID], [t0].[FK_RecordState_SID] AS [FK_RecordState_SID0], [t0].[value], [t0].[SID0] AS [SID00], [t0].[FK_Deviation_SID], [t0].[FK_EnqPropResult_SID], [t0].[SID00] AS [SID000], [t0].[aDescription], [t0].[aName]
FROM [AnswerGrp] AS [a0]
INNER JOIN (
SELECT [e1].[SID], [e1].[FK_AnswerGrp_SID], [e1].[FK_EnqPropertyForm_SID], [e1].[FK_PossResult_SID], [e1].[FK_RecordState_SID], [e1].[value], [t].[SID] AS [SID0], [t].[FK_Deviation_SID], [t].[FK_EnqPropResult_SID], [t].[SID0] AS [SID00], [t].[aDescription], [t].[aName]
FROM [EnqPropResult] AS [e1]
INNER JOIN (
SELECT [e2].[SID], [e2].[FK_Deviation_SID], [e2].[FK_EnqPropResult_SID], [d].[SID] AS [SID0], [d].[aDescription], [d].[aName]
FROM [EnqPropResultDev] AS [e2]
INNER JOIN [Deviation] AS [d] ON [e2].[FK_Deviation_SID] = [d].[SID]
) AS [t] ON [e1].[SID] = [t].[FK_EnqPropResult_SID]
) AS [t0] ON [a0].[SID] = [t0].[FK_AnswerGrp_SID]
) AS [t1] ON [a].[SID] = [t1].[FK_AnswerGrpBatch_SID]
WHERE [e0].[FK_RecordState_SID] = 0
) AS [t2] ON [e].[SID] = [t2].[FK_Enquiry_SID]
WHERE [e].[SID] = 1790797
The problem is that it will use Left Joins for every Include and ThenInclude except the last one. But this will also retrieve information that I do not need. So I tried to make a LINQ query.
Option 2 LINQ
The LINQ query always uses INNER JOINS, so I exactly get what I want from the database. The only problem is that I have no clue how to map this to an Enquiry class where the INNER JOINS are a list within the Enquiry class. Just like with option 1.
var query = from enquiry in _context.Set<Enquiry>()
join enqPartJob in _context.Set<EnqPartJob>()
on enquiry.Id equals enqPartJob.EnquiryId
join answerGrpBatch in _context.Set<AnswerGrpBatch>()
on enqPartJob.Id equals answerGrpBatch.EnqPartJobId
join answerGrp in _context.Set<AnswerGrp>()
on answerGrpBatch.Id equals answerGrp.AnswerGrpBatchId
join enqPropResult in _context.Set<EnqPropResult>()
on answerGrp.Id equals enqPropResult.AnswerGrpId
join enqPropResultDev in _context.Set<EnqPropResultDev>()
on enqPropResult.Id equals enqPropResultDev.EnqPropResultId
join deviation in _context.Set<Deviation>()
on enqPropResultDev.DeviationId equals deviation.Id
where enquiry.Id == id
select new { enquiry, enqPropResultDev };
And yis I get that there is a reason EFCore uses LEFT JOINS but this is not my datbase so I have to adapt to it.
Thx to Ivan Stoev who commented this suggestion.
Starting with Deviation and working my way up to Enquiry did the trick for me.
The query now looks like this (as you can see it contains INNER JOINS).
SELECT [t].[SID], [t].[aDescription], [t].[aName], [t0].[SID], [t0].[FK_Deviation_SID], [t0].[FK_EnqPropResult_SID], [t0].[SID0], [t0].[FK_AnswerGrp_SID], [t0].[FK_EnqPropertyForm_SID], [t0].[FK_PossResult_SID], [t0].[FK_RecordState_SID], [t0].[value], [t0].[SID1], [t0].[FK_AnswerGrpBatch_SID], [t0].[FK_RecordState_SID0], [t0].[SID2], [t0].[FK_EnqPartJob_SID], [t0].[FK_RecordState_SID1], [t0].[SID3], [t0].[FK_Enquiry_SID], [t0].[aName], [t0].[FK_RecordState_SID2], [t0].[remark], [t0].[validFrom], [t0].[validTo], [t0].[SID4], [t0].[FK_CRMPartner_SID], [t0].[FK_Certificate_SID], [t0].[createDate], [t0].[displayDate], [t0].[extID], [t0].[finishDate], [t0].[fixedGenId], [t0].[aName0]
FROM (
SELECT TOP(1) [d].[SID], [d].[aDescription], [d].[aName]
FROM [Deviation] AS [d]
WHERE [d].[SID] = 5038
) AS [t]
LEFT JOIN (
SELECT [e].[SID], [e].[FK_Deviation_SID], [e].[FK_EnqPropResult_SID], [e0].[SID] AS [SID0], [e0].[FK_AnswerGrp_SID], [e0].[FK_EnqPropertyForm_SID], [e0].[FK_PossResult_SID], [e0].[FK_RecordState_SID], [e0].[value], [a].[SID] AS [SID1], [a].[FK_AnswerGrpBatch_SID], [a].[FK_RecordState_SID] AS [FK_RecordState_SID0], [a0].[SID] AS [SID2], [a0].[FK_EnqPartJob_SID], [a0].[FK_RecordState_SID] AS [FK_RecordState_SID1], [e1].[SID] AS [SID3], [e1].[FK_Enquiry_SID], [e1].[aName], [e1].[FK_RecordState_SID] AS [FK_RecordState_SID2], [e1].[remark], [e1].[validFrom], [e1].[validTo], [e2].[SID] AS [SID4], [e2].[FK_CRMPartner_SID], [e2].[FK_Certificate_SID], [e2].[createDate], [e2].[displayDate], [e2].[extID], [e2].[finishDate], [e2].[fixedGenId], [e2].[aName] AS [aName0]
FROM [EnqPropResultDev] AS [e]
INNER JOIN [EnqPropResult] AS [e0] ON [e].[FK_EnqPropResult_SID] = [e0].[SID]
INNER JOIN [AnswerGrp] AS [a] ON [e0].[FK_AnswerGrp_SID] = [a].[SID]
INNER JOIN [AnswerGrpBatch] AS [a0] ON [a].[FK_AnswerGrpBatch_SID] = [a0].[SID]
INNER JOIN [EnqPartJob] AS [e1] ON [a0].[FK_EnqPartJob_SID] = [e1].[SID]
INNER JOIN [Enquiry] AS [e2] ON [e1].[FK_Enquiry_SID] = [e2].[SID]
WHERE [e0].[FK_RecordState_SID] = 0
) AS [t0] ON [t].[SID] = [t0].[FK_Deviation_SID]
ORDER BY [t].[SID], [t0].[SID], [t0].[SID0], [t0].[SID1], [t0].[SID2], [t0].[SID3], [t0].[SID4]

How to improve LINQ statement to use INNER JOIN in resulting SQL statement?

Assuming the following code that applies filtering logic to a passed on collection.
private IQueryable<Customer> ApplyCustomerFilter(CustomerFilter filter, IQueryable<Customer> customers)
{
...
if (filter.HasProductInBackOrder == true)
{
customers = customers.Where(c => c.Orders.Any(o => o.Products.Any(p => p.Status == ProductStatus.BackOrder)))
}
....
return customers;
}
Results in this SQL statement:
SELECT [Extent1].[CustomerId] AS [CustomerId],
[Extent1].[Status] AS [Status]
FROM [Customers] AS [Extent1]
WHERE
(
EXISTS
(
SELECT 1 AS [C1]
FROM
(
SELECT [Extent3].[OrderId] AS [OrderId]
FROM [Orders] AS [Extent3]
WHERE [Extent1].[CustomerId] = [Extent3].[CustomerId]
) AS [Project1]
WHERE EXISTS
(
SELECT 1 AS [C1]
FROM [Products] AS [Extent4]
WHERE ([Project1].[OrderId] = [Extent4].[OrderId])
AND ([Extent4].[Status] = #p__linq__6)
)
)
)
However, I would like to optimize this by forcing to use INNER JOINS so that the result will be similar to this:
SELECT [Extent1].[CustomerId] AS [CustomerId],
[Extent1].[Status] AS [Status]
FROM [Customers] AS [Extent1]
INNER JOIN [Orders] AS [Extent2] ON [Extent1].[CustomerId] = [Extent2].[CustomerId]
INNER JOIN [Products] AS [Extent3] ON [Extent2].[OrderId] = [Extent3].[OrderId]
WHERE [Extent3].[Status] = #p__linq__6
I've tried multiple approaches, but I was unable to accomplish the desired result. Any suggestions on how to force the correct joins and avoiding subselects?

How to do a where in subquery using Entity Framework?

I have a query like:
var result = from u in this.DataContext.Users
join l in DataContext.Locations on u.Id equals l.userId
where u.active == 1
select u;
return result ;
I want to add a subquery WHERE IN clause like:
WHERE u.Id IN (SELECT userId FROM approved_users)
Is this possible?
I am not sure why you want it in a sub query, it seems simpler to just join the Approved Users table, but I do not know the requirement so I have presented two options. One option that has a sub query and one option with the additional join. I am also making an assumption that you don't have any navigation properties.
Option 1 - Subquery:
var subQuery =
from u in context.Users.Where(x => context.ApprovedUsers.Select(y => y.ApprovedUserId).Contains(x.UserId))
join l in context.Locations on u.UserId equals l.UserId
where u.IsActive == true
select u;
which generates something like this
SELECT
[Extent1].[UserId] AS [UserId],
[Extent1].[Name] AS [Name],
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[User] AS [Extent1]
INNER JOIN [dbo].[Location] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[ApprovedUser] AS [Extent3]
WHERE [Extent3].[ApprovedUserId] = [Extent1].[UserId]
)) AND (1 = [Extent1].[IsActive])
Option 2 - Additional Join:
var query =
from u in context.Users
join l in context.Locations on u.UserId equals l.UserId
join au in context.ApprovedUsers on u.UserId equals au.ApprovedUserId
where u.IsActive == true
select u;
which generates:
SELECT
[Extent1].[UserId] AS [UserId],
[Extent1].[Name] AS [Name],
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[User] AS [Extent1]
INNER JOIN [dbo].[Location] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]
INNER JOIN [dbo].[ApprovedUser] AS [Extent3] ON [Extent1].[UserId] = [Extent3].[ApprovedUserId]
WHERE 1 = [Extent1].[IsActive]

Entity Framework Include returning different values for Single and Where

My application can be used to send messages to a combination of single users and teams containing sets of users. The structure is such, that a Message entity has one Recipients entity, which in turn has a list of User entities and a list of Team entities.
I am trying to get a list of Message entities using EF Code First and Linq-to-Entities, and I want to Include the Recipients and Teams to avoid large amounts of lazy loading requests later on.
The strange thing is, the Teams list is always empty if I use the Include clause. After some experimenting, it boils down to this:
var messages = GetAll()
.Include(m => m.Recipients.Teams)
.Where(m => m.Id == 123)
.ToList();
returns a list with one message, where the Teams list is empty. (GetAll() just returns an IQueryable<Message>.) But if I do
var message = GetAll()
.Include(m => m.Recipients.Teams)
.Single(m => m.Id == 123);
then I get the single message, with the Teams correctly populated.
Any ideas why this is happening?
Edit: Here's the generated SQL (taken from Entity Framework Profiler)
Where statement
SELECT *
FROM (SELECT [Extent1].[Id] AS [Id],
[Extent1].[ParentRelation] AS [ParentRelation],
[Extent1].[CreatedUtc] AS [CreatedUtc],
[Extent1].[Subject] AS [Subject],
[Extent1].[Introduction] AS [Introduction],
[Extent1].[Body] AS [Body],
[Extent1].[GlobalId] AS [GlobalId],
[Extent1].[Team_Id] AS [Team_Id],
[Extent1].[Creator_Id] AS [Creator_Id],
[Extent1].[Parent_Id] AS [Parent_Id],
[Extent1].[ReplyTo_Id] AS [ReplyTo_Id],
[Join1].[Id1] AS [Id1],
[Join1].[ToSupervisors] AS [ToSupervisors],
[Join1].[Organisation_Id] AS [Organisation_Id],
[Join1].[Id2] AS [Id2],
[Join4].[Id3] AS [Id3],
[Join4].[Name] AS [Name],
[Join4].[CreatedUtc] AS [CreatedUtc1],
[Join4].[Description] AS [Description],
[Join4].[Color] AS [Color],
[Join4].[Status] AS [Status],
[Join4].[Organisation_Id] AS [Organisation_Id1],
CASE
WHEN ([Join4].[Recipients_Id1] IS NULL) THEN CAST(NULL AS int)
ELSE 1
END AS [C1]
FROM [dbo].[Messages] AS [Extent1]
INNER JOIN (SELECT [Extent2].[Id] AS [Id1],
[Extent2].[ToSupervisors] AS [ToSupervisors],
[Extent2].[Organisation_Id] AS [Organisation_Id],
[Extent3].[Id] AS [Id2]
FROM [dbo].[Recipients] AS [Extent2]
LEFT OUTER JOIN [dbo].[MessageExtensions] AS [Extent3]
ON [Extent2].[Id] = [Extent3].[Recipients_Id]) AS [Join1]
ON [Extent1].[Recipients_Id] = [Join1].[Id1]
LEFT OUTER JOIN (SELECT [Extent4].[Recipients_Id] AS [Recipients_Id1],
[Extent5].[Id] AS [Id3],
[Extent5].[Name] AS [Name],
[Extent5].[CreatedUtc] AS [CreatedUtc],
[Extent5].[Description] AS [Description],
[Extent5].[Color] AS [Color],
[Extent5].[Status] AS [Status],
[Extent5].[Organisation_Id] AS [Organisation_Id],
[Extent6].[Recipients_Id] AS [Recipients_Id2]
FROM [dbo].[RecipientsTeams] AS [Extent4]
INNER JOIN [dbo].[Teams] AS [Extent5]
ON [Extent4].[Team_Id] = [Extent5].[Id]
INNER JOIN [dbo].[MessageExtensions] AS [Extent6]
ON 1 = 1) AS [Join4]
ON ([Extent1].[Recipients_Id] = [Join4].[Recipients_Id2])
AND ([Extent1].[Recipients_Id] = [Join4].[Recipients_Id1])
WHERE 11021 = [Extent1].[Id]) AS [Project1]
ORDER BY [Project1].[Id] ASC,
[Project1].[Id1] ASC,
[Project1].[Id2] ASC,
[Project1].[C1] ASC
Single statement
SELECT *
FROM (SELECT [Limit1].[Id1] AS [Id],
[Limit1].[ParentRelation] AS [ParentRelation],
[Limit1].[CreatedUtc] AS [CreatedUtc],
[Limit1].[Subject] AS [Subject],
[Limit1].[Introduction] AS [Introduction],
[Limit1].[Body1] AS [Body],
[Limit1].[GlobalId1] AS [GlobalId],
[Limit1].[Team_Id] AS [Team_Id],
[Limit1].[Creator_Id] AS [Creator_Id],
[Limit1].[Parent_Id] AS [Parent_Id],
[Limit1].[ReplyTo_Id] AS [ReplyTo_Id],
[Limit1].[Id2] AS [Id1],
[Limit1].[ToSupervisors] AS [ToSupervisors],
[Limit1].[Organisation_Id] AS [Organisation_Id],
[Limit1].[Id3] AS [Id2],
[Join5].[Id4] AS [Id3],
[Join5].[Name] AS [Name],
[Join5].[CreatedUtc1] AS [CreatedUtc1],
[Join5].[Description] AS [Description],
[Join5].[Color] AS [Color],
[Join5].[Status] AS [Status],
[Join5].[Organisation_Id] AS [Organisation_Id1],
CASE
WHEN ([Join5].[Recipients_Id1] IS NULL) THEN CAST(NULL AS int)
ELSE 1
END AS [C1]
FROM (SELECT TOP (2) [Extent1].[Id] AS [Id1],
[Extent1].[ParentRelation] AS [ParentRelation],
[Extent1].[CreatedUtc] AS [CreatedUtc],
[Extent1].[Subject] AS [Subject],
[Extent1].[Introduction] AS [Introduction],
[Extent1].[Body] AS [Body1],
[Extent1].[GlobalId] AS [GlobalId1],
[Extent1].[Team_Id] AS [Team_Id],
[Extent1].[Creator_Id] AS [Creator_Id],
[Extent1].[Parent_Id] AS [Parent_Id],
[Extent1].[ReplyTo_Id] AS [ReplyTo_Id],
[Join1].[Id2],
[Join1].[ToSupervisors],
[Join1].[Organisation_Id],
[Join1].[Id3]
FROM [dbo].[Messages] AS [Extent1]
INNER JOIN (SELECT [Extent2].[Id] AS [Id2],
[Extent2].[ToSupervisors] AS [ToSupervisors],
[Extent2].[Organisation_Id] AS [Organisation_Id],
[Extent3].[Id] AS [Id3]
FROM [dbo].[Recipients] AS [Extent2]
LEFT OUTER JOIN [dbo].[MessageExtensions] AS [Extent3]
ON [Extent2].[Id] = [Extent3].[Recipients_Id]) AS [Join1]
ON [Extent1].[Recipients_Id] = [Join1].[Id2]
WHERE 11021 = [Extent1].[Id]) AS [Limit1]
LEFT OUTER JOIN (SELECT [Extent4].[Recipients_Id] AS [Recipients_Id1],
[Extent5].[Id] AS [Id4],
[Extent5].[Name] AS [Name],
[Extent5].[CreatedUtc] AS [CreatedUtc1],
[Extent5].[Description] AS [Description],
[Extent5].[Color] AS [Color],
[Extent5].[Status] AS [Status],
[Extent5].[Organisation_Id] AS [Organisation_Id],
[Join4].[Id5],
[Join4].[Recipients_Id2]
FROM [dbo].[RecipientsTeams] AS [Extent4]
INNER JOIN [dbo].[Teams] AS [Extent5]
ON [Extent4].[Team_Id] = [Extent5].[Id]
INNER JOIN (SELECT [Extent6].[Id] AS [Id5],
[Extent6].[Recipients_Id] AS [Recipients_Id2]
FROM [dbo].[Messages] AS [Extent6]
LEFT OUTER JOIN [dbo].[MessageExtensions] AS [Extent7]
ON [Extent6].[Recipients_Id] = [Extent7].[Recipients_Id]) AS [Join4]
ON [Extent4].[Recipients_Id] = [Join4].[Recipients_Id2]) AS [Join5]
ON [Limit1].[Id1] = [Join5].[Id5]) AS [Project1]
ORDER BY [Project1].[Id] ASC,
[Project1].[Id1] ASC,
[Project1].[Id2] ASC,
[Project1].[C1] ASC
When I run these queries by hand, I have the same result. For the Where, the Team related properties are all NULL, while for the Single, they are populated.
Edit 2 The GetAll method is a repository method
public virtual IQueryable<T> GetAll()
{
return Context.Set<T>();
}
where T is Message
Can you try this?
var messages = GetAll().Include(m => m.Recipients.Teams) .Where(m => m.Id == 123).Select(m=>m);

EF returns incorrect rows

I have a simple query:
IEnumerable<AmountChangeDocumentItemExtended> GetItemsByHeaderId(Guid headerId){
var results = (from item in _context.AmountChangeDocumentItems
where item.HeaderId == headerId
select new AmountChangeDocumentItemExtended
{.....}).ToArray();
return results;
}
It returns 0 rows, but when i get rows using plain query (ExecuteStoreQuery<>()) i receive actual data.
_context.ExecuteStoreQuery<AmountChangeDocumentItem>(#"SELECT *
FROM
dbo.AmountChangeDocumentItems i
WHERE i.HeaderId = '201dae45-64a6-4b49-b801-c8fb20165ee5'");
i don't understand, why the first method not work?
For any another guid both methods returns correct rows...
In first case i don't see any income queries in my SQL Server profiler...
UPD:
Trace string:
SELECT
[Extent1].[ProductCode] AS [ProductCode],
[Extent1].[Id] AS [Id],
[Extent1].[HeaderId] AS [HeaderId],
[Extent1].[ProductVersion] AS [ProductVersion],
[Extent1].[Amount] AS [Amount],
[Extent1].[PriceCalculatorVersion] AS [PriceCalculatorVersion],
[Extent1].[FirstSupplierPrice] AS [FirstSupplierPrice],
[Extent1].[NonTaxablePart] AS [NonTaxablePart],
[Extent1].[SupplierTaxRate] AS [SupplierTaxRate],
[Extent1].[SupplierInterestRate] AS [SupplierInterestRate],
[Extent1].[RetailTaxRate] AS [RetailTaxRate],
[Extent1].[RetailInterestRate] AS [RetailInterestRate],
[Extent1].[RetailPrice] AS [RetailPrice],
[Extent2].[AgentCode] AS [AgentCode],
[Extent3].[Name] AS [Name],
[Extent1].[ItemOrder] AS [ItemOrder],
[Extent1].[Discount] AS [Discount],
[Extent1].[ExpectedRetailFinalPriceTotal] AS [ExpectedRetailFinalPriceTotal],
[Extent4].[BarCodeString] AS [BarCodeString],
[Extent4].[Name] AS [Name1],
[Extent4].[CountryOfOrigin] AS [CountryOfOrigin],
[Extent4].[UnitOfMeasureCode] AS [UnitOfMeasureCode],
[Extent5].[ShortName] AS [ShortName],
[Extent6].[IsFractional] AS [IsFractional],
[Extent1].[CheckId] AS [CheckId],
CASE WHEN ([Extent1].[CheckId] IS NOT NULL) THEN [Extent7].[Number] END AS [C1],
CASE WHEN ([Extent1].[CheckId] IS NOT NULL) THEN [Extent7].[Sum] END AS [C2],
[Extent1].[AlternateBarCode] AS [AlternateBarCode],
[Extent1].[AlternateBarCodeCoeff] AS [AlternateBarCodeCoeff],
[Extent1].[PalmInitialAmount] AS [PalmInitialAmount],
[Extent1].[PalmInitialRetailPrice] AS [PalmInitialRetailPrice],
[Extent1].[PalmOutAmount] AS [PalmOutAmount],
[Extent1].[PalmOutRetailPrice] AS [PalmOutRetailPrice]
FROM [dbo].[AmountChangeDocumentItems] AS [Extent1]
INNER JOIN [dbo].[CurrentSuppliers] AS [Extent2] ON ([Extent1].[ProductCode] = [Extent2].[ProductCode]) AND ([Extent1].[ProductVersion] = [Extent2].[ProductVersion])
LEFT OUTER JOIN [dbo].[Agents] AS [Extent3] ON [Extent2].[AgentCode] = [Extent3].[Code]
LEFT OUTER JOIN [dbo].[Products] AS [Extent4] ON ([Extent1].[ProductCode] = [Extent4].[InternalCode]) AND ([Extent1].[ProductVersion] = [Extent4].[Version])
LEFT OUTER JOIN [dbo].[UnitsOfMeasure] AS [Extent5] ON [Extent4].[UnitOfMeasureCode] = [Extent5].[Code]
LEFT OUTER JOIN [dbo].[UnitsOfMeasure] AS [Extent6] ON [Extent4].[UnitOfMeasureCode] = [Extent6].[Code]
LEFT OUTER JOIN [dbo].[CheckItems] AS [Extent7] ON [Extent1].[CheckId] = [Extent7].[Id]
WHERE [Extent1].[HeaderId] = #p__linq__0
The problem was in my edmx model: i have incorrect association multiplier (1 - 1 insted of 1 - 0..1), so EF generate INNER JOIN insted of LEFT JOIN.

Categories

Resources