EF returns incorrect rows - c#

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.

Related

LINQ generating Nested/Sub queries

I am using Asp.NET & Entity Framework with SQL Server as Database, somehow I am getting this strange issue
I have this code:
var pricingInfo = (from price in invDB.Pricing.AsNoTracking()
join priceD in invDB.PricingDetail.AsNoTracking() on price.PricingId equals priceDtl.PricingId
join tagD in invDB.PricingTagDetail.AsNoTracking() on priceDtl.PricingDetailId equals tagDtl.PricingDetailId
join it in invDB.Item.AsNoTracking() on tagDtl.ItemId equals item.ItemId
join par in invDB.Party.AsNoTracking() on tagDtl.PartyId equals party.PartyId
join b in invDB.Brand.AsNoTracking() on tagDtl.BrandId equals brd.BrandId into t from brand in t.DefaultIfEmpty()
where tagDtl.AvailableQuantity > 0m && price.PricingNo == printNumber
select new
{
TagNo = tagDtl.TagNo,
SellingRate = tagDtl.SellingRate,
Quantity = tagDtl.AvailableQuantity ?? 0m,
ItemCode = item.Name,
UOMId = priceDtl.UOMId,
Brand = brand.BrandCode,
Supplier = party.PartyCode,
Offer = tagDtl.Offer
}).ToList();
Which generates the below sql query with a sub query, without where condition and it pulls out full records from a large volume data. This results to a heavy memory consumption and performance issues.
SELECT
[Filter1].[PricingId1] AS [PricingId],
[Filter1].[TagNo] AS [TagNo],
[Filter1].[SellingRate1] AS [SellingRate],
CASE WHEN ([Filter1].[AvailableQuantity] IS NULL) THEN cast(0 as decimal(18)) ELSE [Filter1].[AvailableQuantity] END AS [C1],
[Filter1].[Name] AS [Name],
[Filter1].[UOMId 1] AS [UOMId ],
[Extent6].[BrandCode] AS [BrandCode],
[Filter1].[PartyCode] AS [PartyCode],
[Filter1].[Offer] AS [Offer]
FROM
(
SELECT [Extent1].[PricingId] AS [PricingId1], [Extent1].[PricingNo] AS [PricingNo], [Extent2].[UnitOfMeasurementId] AS [UnitOfMeasurementId1], [Extent3].[TagNo] AS [TagNo], [Extent3].[BrandId] AS [BrandId1], [Extent3].[SellingRate] AS [SellingRate1], [Extent3].[AvailableQuantity] AS [AvailableQuantity], [Extent3].[Offer] AS [Offer], [Extent4].[Name] AS [Name], [Extent5].[PartyCode] AS [PartyCode]
FROM [PanERP].[Pricing] AS [Extent1]
INNER JOIN [PanERP].[PricingDetail] AS [Extent2] ON [Extent1].[PricingId] = [Extent2].[PricingId]
INNER JOIN [PanERP].[PricingTagDetail] AS [Extent3] ON [Extent2].[PricingDetailId] = [Extent3].[PricingDetailId]
INNER JOIN [PanERP].[Item] AS [Extent4] ON [Extent3].[ItemId] = [Extent4].[ItemId]
INNER JOIN [PanERP].[Party] AS [Extent5] ON [Extent3].[PartyId] = [Extent5].[PartyId]
WHERE [Extent3].[AvailableQuantity] > cast(0 as decimal(18))
) AS [Filter1]
LEFT OUTER JOIN [PanERP].[Brand] AS [Extent6] ON [Filter1].[BrandId1] = [Extent6].[BrandId]
WHERE ([Filter1].[PricingNo] = #p__linq__0) OR (([Filter1].[PricingNo] IS NULL) AND (#p__linq__0 IS NULL))
But When i change the condition
where tagDtl.AvailableQuantity > 0m
as a variable it creates another SQL query without nested select statement.
Here is the modified code
decimal availableQuantity = 0m;
var pricingInfo = (from price in invDB.Pricing.AsNoTracking()
join priceD in invDB.PricingDetail.AsNoTracking() on price.PricingId equals priceDtl.PricingId
join tagD in invDB.PricingTagDetail.AsNoTracking() on priceDtl.PricingDetailId equals tagDtl.PricingDetailId
join it in invDB.Item.AsNoTracking() on tagDtl.ItemId equals item.ItemId
join par in invDB.Party.AsNoTracking() on tagDtl.PartyId equals party.PartyId
join b in invDB.Brand.AsNoTracking() on tagDtl.BrandId equals brd.BrandId into t from brand in t.DefaultIfEmpty()
where tagDtl.AvailableQuantity > availableQuantity && price.PricingNo == printNumber
select new
{
TagNo = tagDtl.TagNo,
SellingRate = tagDtl.SellingRate,
Quantity = tagDtl.AvailableQuantity ?? availableQuantity,
ItemCode = item.Name,
UOMId = priceDtl.UOMId,
Brand = brand.BrandCode,
Supplier = party.PartyCode,
Offer = tagDtl.Offer
}).ToList();
and here is the SQL query without nested SQL statement.
SELECT
[Extent1].[PricingId] AS [PricingId],
[Extent3].[TagNo] AS [TagNo],
[Extent3].[SellingRate] AS [SellingRate],
CASE WHEN ([Extent3].[AvailableQuantity] IS NULL) THEN cast(0 as decimal(18)) ELSE [Extent3].[AvailableQuantity] END AS [C1],
[Extent4].[Name] AS [Name],
[Extent2].[UOMId ] AS [UOMId ],
[Extent6].[BrandCode] AS [BrandCode],
[Extent5].[PartyCode] AS [PartyCode],
[Extent3].[Offer] AS [Offer]
FROM [PanERP].[Pricing] AS [Extent1]
INNER JOIN [PanERP].[PricingDetail] AS [Extent2] ON [Extent1].[PricingId] = [Extent2].[PricingId]
INNER JOIN [PanERP].[PricingTagDetail] AS [Extent3] ON [Extent2].[PricingDetailId] = [Extent3].[PricingDetailId]
INNER JOIN [PanERP].[Item] AS [Extent4] ON [Extent3].[ItemId] = [Extent4].[ItemId]
INNER JOIN [PanERP].[Party] AS [Extent5] ON [Extent3].[PartyId] = [Extent5].[PartyId]
LEFT OUTER JOIN [PanERP].[Brand] AS [Extent6] ON [Extent3].[BrandId] = [Extent6].[BrandId]
WHERE ([Extent3].[AvailableQuantity] > #p__linq__0) AND (([Extent1].[PricingNo] = #p__linq__1) OR (([Extent1].[PricingNo] IS NULL) AND (#p__linq__1 IS NULL)))
If I move the where condition to the model definition as lambda expression, like this
from price in inventoryDb.Pricing.AsNoTracking().Where(c =>
c.PricingNo == printNumber))
then also it works fine.
Why is LINQ generating a nested Select? How can we avoid this?
Thanks in advance for your answers.
Well, I think you have answered your own question, on your comments. I will just try to clarify what is going on.
When you use a hard-coded constant, like 0m, the framework translates it into SQL keeping the value as a constant:
WHERE [Extent3].[AvailableQuantity] > cast(0 as decimal(18))
When you use a local variable, like “availableQuantity”, the framework creates a parameter:
([Extent3].[AvailableQuantity] > #p__linq__0)
I might be wrong, but, as I see, this is done in order to preserve the programmer’s goal when writing the code (constant = constant, variable = parameter).
And what about the subquery?
This is a query optimization logic (a bad one, probably, at least on this scenario). When you make a query using parameters, you might run it several times, but SQL Server will always use the same execution plan, making the query faster; when you use constants, each query need to be reevaluated (if you check SQL Server Activity Monitor, you will see that queries with parameters are treated as the same query, regardless the parameters values).
This way, in my opinion (sorry, I could not find any documentation about it), Entity Framework is trying to isolate the queries; the outer/generic one, that use parameters, and the inner/specific one, that use constants.
I would be happy if anyone could complement it with some Microsoft documentation about this subject…

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);

Limit results from multiple individual tables in a single LINQ-to-Entities query. Resultant T-SQL is wrong

I need to query multiple tables with one query, and I need to limit the results from each table individually.
An example ...
I have a ContentItem, Retailer, and Product table.
ContentItem has a Type (int) field that corresponds to an enum of content types like "Retailer" and "Product." I am filtering ContentItem using this field for each sub-subquery.
ContentItem has an Id (pkey) field.
Retailer and Product have an Id (pkey) field. Id is also an FK to ContentItem.Id.
I can select from all three tables with a LEFT JOIN query. From there, I can then limit the total number of rows returned, let's say 6 rows total.
What I want to do is limit the number of rows returned from Retailer and Product individually. This way, I will have 12 rows (max) total: 6 from Retailer, and 6 from Product.
I can already accomplish this with SQL, but I am having a difficult time getting LINQ-to-Entities to "do the right thing."
Here's my SQL
SELECT * From
(
(SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 0 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Retailers)
UNION ALL
(SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 1 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Brands)
UNION ALL
(SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 2 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Products)
UNION ALL
(SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 3 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Certifications)
UNION ALL
(SELECT * FROM (SELECT * FROM [dbo].[ContentItem] WHERE Type = 4 ORDER BY ContentItem.mtime OFFSET 0 ROWS FETCH NEXT 6 ROWS ONLY) Claims)
) as ContentItem
LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id);
Here's one of my many iterations of LINQ queries (which is not returning the desired result).
var queryRetailers = contentItemModel
.Where(contentItem => contentItem.Type == ContentTypeEnum.Retailer)
.OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryBrands = contentItemModel
.Where(contentItem => contentItem.Type == ContentTypeEnum.Brand)
.OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryProducts = contentItemModel
.Where(contentItem => contentItem.Type == ContentTypeEnum.Product)
.OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryCertifications = contentItemModel
.Where(contentItem => contentItem.Type == ContentTypeEnum.Certification)
.OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var queryClaims = contentItemModel
.Where(contentItem => contentItem.Type == ContentTypeEnum.Claim)
.OrderByDescending(o => o.mtime).Skip(skip).Take(take).Select(o => new { Id = o.Id });
var query = from contentItem in
queryRetailers
.Concat(queryBrands)
.Concat(queryProducts)
.Concat(queryCertifications)
.Concat(queryClaims)
join item in context.Retailer on contentItem.Id equals item.Id into retailerGroup
from retailer in retailerGroup.DefaultIfEmpty(null)
join item in context.Brand on contentItem.Id equals item.Id into brandGroup
from brand in brandGroup.DefaultIfEmpty(null)
join item in context.Product on contentItem.Id equals item.Id into productGroup
from product in productGroup.DefaultIfEmpty(null)
join item in context.Certification on contentItem.Id equals item.Id into certificationGroup
from certification in certificationGroup.DefaultIfEmpty(null)
join item in context.Claim on contentItem.Id equals item.Id into claimGroup
from claim in claimGroup.DefaultIfEmpty(null)
select new
{
contentItem,
retailer,
brand,
product,
certification,
claim
};
var results = query.ToList();
This query returns SQL that essentially "nests" my UNION ALL statements, and the server returns all rows from the database.
SELECT
[Distinct4].[C1] AS [C1],
[Distinct4].[C2] AS [C2],
[Extent6].[Id] AS [Id],
[Extent6].[RowVersion] AS [RowVersion],
[Extent6].[ctime] AS [ctime],
[Extent6].[mtime] AS [mtime],
[Extent7].[Id] AS [Id1],
[Extent7].[Recommended] AS [Recommended],
[Extent7].[RowVersion] AS [RowVersion1],
[Extent7].[ctime] AS [ctime1],
[Extent7].[mtime] AS [mtime1],
[Extent8].[Id] AS [Id2],
[Extent8].[OverrideGrade] AS [OverrideGrade],
[Extent8].[PlantBased] AS [PlantBased],
[Extent8].[Recommended] AS [Recommended1],
[Extent8].[RowVersion] AS [RowVersion2],
[Extent8].[ctime] AS [ctime2],
[Extent8].[mtime] AS [mtime2],
[Extent8].[Brand_Id] AS [Brand_Id],
[Extent8].[Grade_Name] AS [Grade_Name],
[Extent8].[Grade_Value] AS [Grade_Value],
[Extent9].[Id] AS [Id3],
[Extent9].[RowVersion] AS [RowVersion3],
[Extent9].[ctime] AS [ctime3],
[Extent9].[mtime] AS [mtime3],
[Extent9].[Grade_Name] AS [Grade_Name1],
[Extent9].[Grade_Value] AS [Grade_Value1],
[Extent10].[Id] AS [Id4],
[Extent10].[RowVersion] AS [RowVersion4],
[Extent10].[ctime] AS [ctime4],
[Extent10].[mtime] AS [mtime4],
[Extent10].[Grade_Name] AS [Grade_Name2],
[Extent10].[Grade_Value] AS [Grade_Value2]
FROM (SELECT DISTINCT
[UnionAll4].[C1] AS [C1],
[UnionAll4].[C2] AS [C2]
FROM (SELECT
[Distinct3].[C1] AS [C1],
[Distinct3].[C2] AS [C2]
FROM ( SELECT DISTINCT
[UnionAll3].[C1] AS [C1],
[UnionAll3].[C2] AS [C2]
FROM (SELECT
[Distinct2].[C1] AS [C1],
[Distinct2].[C2] AS [C2]
FROM ( SELECT DISTINCT
[UnionAll2].[C1] AS [C1],
[UnionAll2].[C2] AS [C2]
FROM (SELECT
[Distinct1].[C1] AS [C1],
[Distinct1].[C2] AS [C2]
FROM ( SELECT DISTINCT
[UnionAll1].[C1] AS [C1],
[UnionAll1].[Id] AS [C2]
FROM (SELECT TOP (1000)
[Project1].[C1] AS [C1],
[Project1].[Id] AS [Id]
FROM ( SELECT [Project1].[Id] AS [Id], [Project1].[mtime] AS [mtime], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[mtime] DESC) AS [row_number]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[mtime] AS [mtime],
1 AS [C1]
FROM [dbo].[ContentItem] AS [Extent1]
WHERE 0 = CAST( [Extent1].[Type] AS int)
) AS [Project1]
) AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[mtime] DESC
UNION ALL
SELECT TOP (1000)
[Project3].[C1] AS [C1],
[Project3].[Id] AS [Id]
FROM ( SELECT [Project3].[Id] AS [Id], [Project3].[mtime] AS [mtime], [Project3].[C1] AS [C1], row_number() OVER (ORDER BY [Project3].[mtime] DESC) AS [row_number]
FROM ( SELECT
[Extent2].[Id] AS [Id],
[Extent2].[mtime] AS [mtime],
1 AS [C1]
FROM [dbo].[ContentItem] AS [Extent2]
WHERE 1 = CAST( [Extent2].[Type] AS int)
) AS [Project3]
) AS [Project3]
WHERE [Project3].[row_number] > 0
ORDER BY [Project3].[mtime] DESC) AS [UnionAll1]
) AS [Distinct1]
UNION ALL
SELECT TOP (1000)
[Project7].[C1] AS [C1],
[Project7].[Id] AS [Id]
FROM ( SELECT [Project7].[Id] AS [Id], [Project7].[mtime] AS [mtime], [Project7].[C1] AS [C1], row_number() OVER (ORDER BY [Project7].[mtime] DESC) AS [row_number]
FROM ( SELECT
[Extent3].[Id] AS [Id],
[Extent3].[mtime] AS [mtime],
1 AS [C1]
FROM [dbo].[ContentItem] AS [Extent3]
WHERE 2 = CAST( [Extent3].[Type] AS int)
) AS [Project7]
) AS [Project7]
WHERE [Project7].[row_number] > 0
ORDER BY [Project7].[mtime] DESC) AS [UnionAll2]
) AS [Distinct2]
UNION ALL
SELECT TOP (1000)
[Project11].[C1] AS [C1],
[Project11].[Id] AS [Id]
FROM ( SELECT [Project11].[Id] AS [Id], [Project11].[mtime] AS [mtime], [Project11].[C1] AS [C1], row_number() OVER (ORDER BY [Project11].[mtime] DESC) AS [row_number]
FROM ( SELECT
[Extent4].[Id] AS [Id],
[Extent4].[mtime] AS [mtime],
1 AS [C1]
FROM [dbo].[ContentItem] AS [Extent4]
WHERE 3 = CAST( [Extent4].[Type] AS int)
) AS [Project11]
) AS [Project11]
WHERE [Project11].[row_number] > 0
ORDER BY [Project11].[mtime] DESC) AS [UnionAll3]
) AS [Distinct3]
UNION ALL
SELECT TOP (1000)
[Project15].[C1] AS [C1],
[Project15].[Id] AS [Id]
FROM ( SELECT [Project15].[Id] AS [Id], [Project15].[mtime] AS [mtime], [Project15].[C1] AS [C1], row_number() OVER (ORDER BY [Project15].[mtime] DESC) AS [row_number]
FROM ( SELECT
[Extent5].[Id] AS [Id],
[Extent5].[mtime] AS [mtime],
1 AS [C1]
FROM [dbo].[ContentItem] AS [Extent5]
WHERE 4 = CAST( [Extent5].[Type] AS int)
) AS [Project15]
) AS [Project15]
WHERE [Project15].[row_number] > 0
ORDER BY [Project15].[mtime] DESC) AS [UnionAll4] ) AS [Distinct4]
LEFT OUTER JOIN [dbo].[Retailer] AS [Extent6] ON [Distinct4].[C2] = [Extent6].[Id]
LEFT OUTER JOIN [dbo].[Brand] AS [Extent7] ON [Distinct4].[C2] = [Extent7].[Id]
LEFT OUTER JOIN [dbo].[Product] AS [Extent8] ON [Distinct4].[C2] = [Extent8].[Id]
LEFT OUTER JOIN [dbo].[Certification] AS [Extent9] ON [Distinct4].[C2] = [Extent9].[Id]
LEFT OUTER JOIN [dbo].[Claim] AS [Extent10] ON [Distinct4].[C2] = [Extent10].[Id]
So my overall questions are:
1) Is there a simpler SQL query I can execute to get the same results? I know that T-SQL doesn't support offsets per table in a subquery, hence the subquery wrapping.
2) If there isn't, what am I doing wrong in my LINQ query? Is this even possible with LINQ?
I wanted to add the SQL from #radar here all nice and formatted. It at least appears to be an elegant solution to avoid the sub-subqueries, and still accomplishes the offset/fetch.
SELECT *
FROM (SELECT
[ContentItem].*,
row_number() OVER ( PARTITION BY Type ORDER BY ContentItem.mtime ) as rn
FROM [dbo].[ContentItem]
LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id)
) as x
WHERE x.rn >= a AND x.rn <= b;
a is the lower threshold (offset) and b is the upper threshold (fetch-ish). The only catch is that b now equals fetch + a instead of just fetch. The first set of results would be WHERE x.rn >= 0 AND x.rn <= 6, the second set WHERE x.rn >= 6 AND x.rn <= 12, third WHERE x.rn >= 12 AND x.rn <= 18, and so on.
As you are looking simpler SQL, you can use row_number analytic function, which would be faster
You need to try and see as there are many left joins and also proper index need to exists in these tables.
select *
from (
select *, row_number() over ( partition by Type order by ContentItem.mtime ) as rn
from [dbo].[ContentItem]
LEFT JOIN [dbo].[Retailer] ON (Retailer.Id = ContentItem.Id)
LEFT JOIN [dbo].[Brand] ON (Brand.Id = ContentItem.Id)
LEFT JOIN [dbo].[Product] ON (Product.Id = ContentItem.Id)
LEFT JOIN [dbo].[Certification] ON (Certification.Id = ContentItem.Id)
LEFT JOIN [dbo].[Claim] ON (Claim.Id = ContentItem.Id);
)
where rn <= 6
Well, it appears that I'm an idiot. That TOP(1000) call should have tipped me off. I assumed that my take variable was set to 6 but it was, in fact, set to 1000. Turns out my giant LINQ query works as expected, but the nested UNION ALL statements threw me off.
Still, I'm going to investigate #radar's answer further. It's hard to argue with better performance.

Entity Framework 6 too many joins

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.

Linq : INNER JOIN after WHERE and not before

I have a query like that :
context.Diffusions.Where(x => x.ProgrammeId == programmeID).Include("Chaines").Include("Version").ToList();
The query generated is:
SELECT
[Extent1].[Duree] AS [Duree],
[Extent1].[Id] AS [Id],
[Extent1].[ProgrammeId] AS [ProgrammeId],
[Extent1].[VersionId] AS [VersionId],
[Extent1].[ChaineId] AS [ChaineId],
[Extent1].[Debut] AS [Debut],
[Extent1].[Fin] AS [Fin],
[Extent1].[ReRun] AS [ReRun],
[Extent1].[DateModification] AS [DateModification],
[Extent1].[DateDiffusion] AS [DateDiffusion],
[Extent2].[Id] AS [Id1],
[Extent2].[Nom] AS [Nom],
[Extent2].[Code] AS [Code],
[Extent2].[Abreviation] AS [Abreviation],
[Extent3].[Id] AS [Id2],
[Extent3].[ProgrammeId] AS [ProgrammeId1],
[Extent3].[CleVersion] AS [CleVersion],
[Extent3].[Numero] AS [Numero],
[Extent3].[NumeroModification] AS [NumeroModification],
[Extent3].[VO] AS [VO],
[Extent3].[TitrePresse] AS [TitrePresse],
[Extent3].[Description] AS [Description],
[Extent3].[Remarque] AS [Remarque],
[Extent3].[SousTitre] AS [SousTitre],
[Extent3].[DureeTheorique] AS [DureeTheorique],
[Extent3].[Format] AS [Format],
[Extent3].[Interdit] AS [Interdit],
[Extent3].[LangueId] AS [LangueId],
[Extent3].[TypeCoteDiffusionId] AS [TypeCoteDiffusionId]
FROM [dbo].[Diffusion] AS [Extent1]
INNER JOIN [dbo].[Chaine] AS [Extent2] ON [Extent1].[ChaineId] = [Extent2].[Id]
INNER JOIN [dbo].[Version] AS [Extent3] ON [Extent1].[VersionId] = [Extent3].[Id]
WHERE [Extent1].[ProgrammeId] = 1926475
My problem is that the Table as a lot of entries and it makes an inner join for each entry and then do the "WHERE" so it takes like 6sec.
When I do the query without the include it's instant. I would like to have a linq query that do the "WHERE" and then the "INCLUDE" for each row returned without having to do it manually for each entry (a Programme can have like 1 000 diffusions).
try this Code And use Contains
for example follow code
context.Diffusions.Where(x => x.ProgrammeId == programmeID).Contains("Chaines").Include("Version").ToList();

Categories

Resources