EF Core 3.1 produces incorrect query translation - sql-server

I have the following EF Core 3.1 statement:
var orders = await _dbContext.Orders.Include(o => o.OrderReceivers)
.Where(o => o.BooleanFlag1 && o.OrderState == OrderState.SomeState && o.OrderReceivers.Any(o => o.BooleanFlag2))
.ToListAsync(cancellationToken);
and this is the generated SQL Server query:
SELECT /*All column names here*/
FROM [Schema].[Orders] AS [o]
LEFT JOIN [Schema].[OrderReceivers] AS [o0] ON [o].[Id] = [o0].[OrderId]
WHERE (([o].[ShouldSendBlackList] = CAST(1 AS bit)) AND ([o].[OrderState] = 2)) AND EXISTS (
SELECT 1
FROM [Schema].[OrderReceivers] AS [o1]
WHERE ([o].[Id] = [o1].[OrderId]) AND ([o1].[BooleanFlag2] = '1'))
ORDER BY [o].[Id], [o0].[Id]
The problem with the generated SQL Query is this part:
([o1].[BooleanFlag2] = '1')
Because this causes the second where clause to be always false!
Any ideas on what is wrong here? Thanks in advance.

Related

EF6 query that behaves strangely using contains

I have an EF6 SQL Server query that behaves strangely when it is supplied with a List<int> of IDs to use. If bookGenieCategory = a value it works. If selectedAges is empty (count = 0) all is well. If the selectedAges contains values that exist in the ProductCategory.CategoryId column, the contains fails and NO rows are returned.
Note: AllocationCandidates is a view, which works properly on its own.
CREATE VIEW dbo.AllocationCandidate
AS
SELECT
p.ProductID, p.SKU as ISBN, p.Name as Title,
pv.MSRP, pv.Price, pv.VariantID, pv.Inventory,
ISNULL(plt.DateLastTouched, GETDATE()) AS DateLastTouched,
JSON_VALUE(p.MiscText, '$.AgeId') AS AgeId,
JSON_VALUE(p.MiscText, '$.AgeName') AS AgeName
FROM
dbo.Product AS p WITH (NOLOCK)
INNER JOIN
dbo.ProductVariant AS pv WITH (NOLOCK) ON pv.ProductID = p.ProductID
LEFT OUTER JOIN
dbo.KBOProductLastTouched AS plt WITH (NOLOCK) ON plt.ProductID = p.ProductID
WHERE
(ISJSON(p.MiscText) = 1)
AND (p.Deleted = 0)
AND (p.Published = 1)
AND (pv.IsDefault = 1)
GO
Do I have a typo here or a misplaced parenthesis in the following query?
var returnList = (from ac in _db.AllocationCandidates
join pc in _db.ProductCategories on ac.ProductID equals pc.ProductID
where (bookGenieCategory == 0
|| bookGenieCategory == pc.CategoryID)
&&
(selectedAges.Count == 0 ||
selectedAges.Contains(pc.CategoryID))
orderby ac.AgeId, ac.DateLastTouched descending
select ac).ToList();
Firstly, I would recommend extracting the conditionals outside of the Linq expression. If you only want to filter data if a value is provided, move the condition check outside of the Linq rather than embedding it inside the condition. This is generally easier to do with the Fluent Linq than Linq QL. You should also aim to leverage navigation properties for relationships between entities. This way an AllocationCandidate should have a collection of ProductCategories:
var query = _db.AllocationCandidates.AsQueryable();
if (bookGenieCategory != 0)
query = query.Where(x => x.ProductCategories.Any(c => c.CategoryID == bookGenieCategory);
The next question is what does the selectedAges contains? There is an Age ID on the AllocationCandidate, but your original query is checking against the ProductCategory.CategoryId??
If the check should be against the AllocationCandidate.AgeId:
if (selectedAges.Any())
query = query.Where(x => selectedAges.Contains(x.AgeID));
If the check is as you wrote it against the ProductCategory.CategoryId:
if (selectedAges.Any())
query = query.Where(x => x.ProductCategories.Any(c => selectedAges.Contains(c.AgeID)));
Then add your order by and get your results:
var results = query.OrderBy(x => x.AgeId)
.ThenByDescending(x => x.DateLastTouched);
.ToList();

Why does LINQ generated SQL include multiple "IS NULL" conditions for the same column

The following query against a SQL Server 2012 database, using Entity Framework Core 3.1:
var tows = await _context.DataEntryTow
.Where(t => _context.DataEntrySample
.Any(s => s.TowId==t.TowId && (s.MicroscopeId != "0" || s.MicroscopeId == null)))
.Select (t => new { text = t.TowId, value = t.TowId });
generates this SQL:
SELECT d.tow_id AS text
FROM data_entry_tow AS d
WHERE EXISTS (
SELECT 1
FROM data_entry_sample AS d0
WHERE (d0.tow_id = d.tow_id) AND (((d0.microscope_id <> '0') OR (d0.microscope_id IS NULL)) OR (d0.microscope_id IS NULL)))
I don't think I've done anything wrong, and I'm fairly sure the query optimizer will eliminate the second (d0.microscope_id IS NULL), but it still seems like an error in the LINQ code.
MicroscopeId is defined:
public string MicroscopeId { get; set; }
Field MicroscopeId declared as nullable. So, to mimic LINQ to Objects behaviour when null != "0" is true but in SQL null <> '0' is false, EF Core generates additional OR condition.
To disable this geneeration you have to specify that when building DbContextOptions:
optionsBuilder.UseSqlServer(constr, b => b.UseRelationalNulls(true) );
Additional info: https://learn.microsoft.com/en-us/ef/core/querying/null-comparisons#using-relational-null-semantics

EF6, Linq to entities queries all columns with select new clause

Not sure what is going on here, but my query is using every column even though I'm using a projection.
var students = (from x in db.vStudentProfiles
where x.strLegalFirstName == searchDTO.FirstName &&
x.strLegalMiddleName == searchDTO.MiddleName &&
x.strLegalLastName == searchDTO.LastName &&
x.datBirthDate == searchDTO.DateOfBirth &&
x.intStudentStatusCd != 3
orderby x.intStudentStatusCd ascending, x.datCreated descending
select new
{
x.strLegalFirstName,
x.strLegalLastName,
x.strLegalMiddleName,
x.datBirthDate,
x.intStudentStatusCd,
x.datCreated,
x.strNBEN
}
).ToList();
which results in all columns getting queried:
SELECT
[Project1].[C1] AS [C1],
[Project1].[strLegalFirstName] AS [strLegalFirstName],
[Project1].[strLegalLastName] AS [strLegalLastName],
[Project1].[strLegalMiddleName] AS [strLegalMiddleName],
[Project1].[datBirthDate] AS [datBirthDate],
[Project1].[intStudentStatusCd] AS [intStudentStatusCd],
[Project1].[datCreated] AS [datCreated],
[Project1].[strNBEN] AS [strNBEN]
FROM ( SELECT
[Extent1].[strNBEN] AS [strNBEN],
[Extent1].[strLegalFirstName] AS [strLegalFirstName],
[Extent1].[strLegalMiddleName] AS [strLegalMiddleName],
[Extent1].[strLegalLastName] AS [strLegalLastName],
[Extent1].[datBirthDate] AS [datBirthDate],
[Extent1].[datCreated] AS [datCreated],
[Extent1].[intStudentStatusCd] AS [intStudentStatusCd],
1 AS [C1]
FROM (SELECT
[vStudentProfile].[intStudentId] AS [intStudentId],
[vStudentProfile].[strStudentStatusDescE] AS [strStudentStatusDescE],
[vStudentProfile].[strSPID] AS [strSPID],
[vStudentProfile].[strSPIDPart1] AS [strSPIDPart1],
[vStudentProfile].[strSPIDPart3] AS [strSPIDPart3],
[vStudentProfile].[strSPIDPart4] AS [strSPIDPart4],
........ bunch of other columns
[vStudentProfile].[strStreetDirection] AS [strStreetDirection],
[vStudentProfile].[strProvince] AS [strProvince],
[vStudentProfile].[intStudentStatusCd] AS [intStudentStatusCd],
[vStudentProfile].[intSiteId] AS [intSiteId]
FROM [dbo].[vStudentProfile] AS [vStudentProfile]) AS [Extent1]
WHERE (([Extent1].[strLegalFirstName] = #p__linq__0) OR (([Extent1].[strLegalFirstName] IS NULL) AND (#p__linq__0 IS NULL))) AND (([Extent1].[strLegalMiddleName] = #p__linq__1) OR (([Extent1].[strLegalMiddleName] IS NULL) AND (#p__linq__1 IS NULL))) AND (([Extent1].[strLegalLastName] = #p__linq__2) OR (([Extent1].[strLegalLastName] IS NULL) AND (#p__linq__2 IS NULL))) AND ([Extent1].[datBirthDate] = #p__linq__3) AND (3 <> [Extent1].[intStudentStatusCd])
) AS [Project1]
ORDER BY [Project1].[intStudentStatusCd] ASC, [Project1].[datCreated] DESC
I've also tried
var students = (from x in db.vStudentProfiles
let Child = new
{
x.strLegalFirstName,
.....
orderby x.intStudentStatusCd ascending, x.datCreated descending
select Child
).ToList();
but as I expected didn't make a difference.
Use edmx with a single SQL Server View mapped.
Not sure what is going on here, but my query is using every column even though I'm using a projection.
That's just the way EF generates queries. EF expects the database engine to be smart enough to ignore the columns that are not used. SQL Server is smart enough.

Convert a SQL query to EntityFramework

How can I convert this query to Entity Framework Query?
select Price,
(
select Cost.Title
from Cost
where Cost.CostID= CostItem.CostID
) as nameCost
from CostItem
where ItemID= 11
var result = dbContext.CostItems
.Where(item => item.ItemId == 11)
.Select(item => new { Price = item.Price, nameCose = item.Cost.Title })
Just make sure you have you relations in entity objects properly set up
Your query can also be written with a LEFT JOIN:
SELECT
ci.Price,
NameCost = c.Title
FROM CostItem ci
LEFT JOIN Cost c
ON c.CostID = ci.CostID
WHERE ci.ItemID = 11
Converting this to Linq using C#, you get:
var t =
from ci in CostItem
join c in Cost on ci.CostID equals c.CostID into lc
from c in lc.DefaultIfEmpty()
where ci.ItemID == 11
select new {
Price = ci.Price,
NameCost = c.Title
};

UseDatabaseNullSemantics not working

this is the query I am trying to execute:
int? i = default(int);
var orders= db.orders.Include(p => p.regions).Include(p => p.types);
int? i = default(int);
var list = orders
.Where(item => item.visible== true && ( (item.children== true) || ((item.dad== i) && item.children== false) )).OrderByDescending(item => item.year);
db is a IdentityDbContext; in its constructor I set
this.Configuration.UseDatabaseNullSemantics = true;
The generated query is the following:
SELECT
[Project1].[id] AS [id],
[Project1].[visible] AS [visible],
[Project1].[dad] AS [dad],
[Project1].[children] AS [children],
FROM ( SELECT
[Extent1].[id] AS [id],
[Extent1].[visible] AS [visible],
[Extent1].[dad] AS [dad],
[Extent1].[children] AS [children],
[Extent1].[n_unita] AS [n_unita]
FROM [dbo].[orders] AS [Extent1]
WHERE (1 = [Extent1].[visible]) AND ((1 = [Extent1].[children]) OR (([Extent1].[dad] = #p__linq__0) AND (0 = [Extent1].[children])))
) AS [Project1]
ORDER BY [Project1].[year] DESC
The problem is that with item.dad want to check also if dad is NULL, but this is not happening and I cannot understand why, where am I mistaken?
Nothing changes with this.Configuration.UseDatabaseNullSemantics = false;
This answer gave me the solution: link to answer.
There is nothing wrong with this code, a change had to be made in the class extending IdentityDbContext in order to make ef generate the correct IS NULL check.
Dad field is a foreign key and I made the mistake to tell the code that is a required field, while it is not, so I corrected the code in the following way:
modelBuilder.Entity<orders>()
.HasMany(e => e.order)
.WithOptional(e => e.dad_orders)
.HasForeignKey(e => e.dad)
.WillCascadeOnDelete(false);

Resources