I would like to get all lines
from a table where column: CODEFIN (varchar) not like "MIR" or "CED".
My table looks like this:
CODEFIN
ID
NULL
2
NULL
3
MIR
2
My Entity Framwork linq request is:
db.MYTABLE.Where(m => m.CODEFIN !="MIR" && m.CODEFIN != "CED") [...]
But CODEFIN null lines not appear...
I get the generated sql, but this is the same things in my sql editor.
the sql is like this:
SELECT
[Extent1].[CODEFIN] AS [CODEFIN],
[Extent1].[ID] AS [ID],
FROM [dbo].[MYTABLE] AS [Extent1]
WHERE ('MIR' <> [Extent1].[CODEFIN] AND 'CED' <> [Extent1].[CODEFIN])
Thank you.
Sylvain
I would do it as
db.MYTABLE.Where(m => m.CODEFIN == NULL ||
(m.CODEFIN !="MIR" && m.CODEFIN != "CED"))
or use Contains:
db.MYTABLE.Where(m => m.CODEFIN == NULL ||
!(new [] {"MIR", "CED"}.Contains(m.CODEFIN)))
Which would yield the SQL:
WHERE [Extent1].[CODEFIN] IS NULL OR ([Extent1].[CODEFIN] NOT IN ('MIR', 'CED'))
Comparing something (even NULL) to NULL always yields NULL, not FALSE, and NOT(NULL) is still NULL, not TRUE, so you have to be more explicit about checking for nulls.
Add:
&& m.CODEFIN == null
A sometimes more efficient solution is to use Except
db.MYTABLE.Where(m => new [] {"MIR", "CED"}
.Except(new[] {m.CODEFIN})
.Any())
This should hopefully result in the following SQL
WHERE EXISTS (
SELECT 'MIR'
UNION
SELECT 'CED'
EXCEPT
SELECT [Extent1].[CODEFIN]
)
This is pretty efficient, as shown here.
Related
I have a weird behaviour from Entity Framework 6. I have a simple (a simple where and a simple select) query which takes 30s.
I used Sql Profiler to watch what sql code is executed. I am using a Where then the FirstOrDefault method to get an item. Then I tried another query, I did a ToList (to fetch data) then FirstOrDefault and it takes less than 1 second.
Original code (takes 30s to be executed):
-----------------------------------------
id = Container.SocialNetworks.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId).Select(i => i.UserID).FirstOrDefault();
From SQL Profiler :
-------------------
exec sp_executesql N'SELECT
[Limit1].[UserID] AS [UserID]
FROM ( SELECT TOP (1)
[Extent1].[UserID] AS [UserID]
FROM [dbo].[SocialNetworks] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
WHERE (0 = [Extent1].[SocialNetwork]) AND (([Extent1].[Link] = #p__linq__0) OR (([Extent1].[Link] IS NULL) AND (#p__linq__0 IS NULL))) AND ([Extent2].[TenantID] = #p__linq__1)
) AS [Limit1]',N'#p__linq__0 nvarchar(4000),#p__linq__1 int',#p__linq__0=N'linkedin.com/in/a-profile',#p__linq__1=5
After testing another solutions (takes less than 1s):
-----------------------------------------------------
id = Container.SocialNetworks.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId).Select(i => i.UserID).ToList().FirstOrDefault();
From SQL Profiler:
------------------
exec sp_executesql N'SELECT
[Extent1].[UserID] AS [UserID]
FROM [dbo].[SocialNetworks] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
WHERE (0 = [Extent1].[SocialNetwork]) AND (([Extent1].[Link] = #p__linq__0) OR (([Extent1].[Link] IS NULL) AND (#p__linq__0 IS NULL))) AND ([Extent2].[TenantID] = #p__linq__1)',N'#p__linq__0 nvarchar(4000),#p__linq__1 int',#p__linq__0=N'linkedin.com/in/a-profile-as',#p__linq__1=5
As you can see, I use ToList to fetch data before filtering with FirstOrDefault. And, normally, it is not advisable to do a ToList, a eager load. Why Entity Framework put a select into a select when I use FirstOrDefault ?
I am sorry for my english and I hope I explained properly my issue.
EDIT :
I have something interesting to add, when the "linkedinurl" value does not exist, and only when it does not exist, in the database, both queries take less than 1 second.
EDITÂ 2:
After writing a comment, I would like to add that our database is on Azure. And the problem does not appear on a simple SQLEXPRESS database. Moreover, this issue appeared like 4 or 5 days ago.
That is because you use FirstOrDefault AFTER the where().Select() combination.
The first query would work better like this :
id = Container.SocialNetworks.FirstOrDefault(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)?.UserID;
As you can see, I use FirstOrDefault just like you used your Where, but this will load the entire object, as discussed in the comments.
Why is your seconde query faster ? Because you ended the query with a ToList() so the FirstOrDefault part apply only in your c# code, AFTER the lines have been loaded, not on the DB with a double select.
Edit :
Trying these 2 lines might highlight the root cause better :
1. Try to order your set :
id = Container.SocialNetworks
.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)
.OrderBy(t => t.UserID).Select(i => i.UserID).FirstOrDefault();
2. Use an aggregate function :
id = Container.SocialNetworks
.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)
.Min(i => i.UserID);
I have the following compiled Linq query:
public static readonly Func<DBContext, Models.User, Type, ObjectType, int?, UserNotification> GetUnreadNotificationID =
CompiledQuery.Compile((DBContext db, Models.User forUser, Type notificationType, ObjectType forObjectType, int? forObjectID) =>
db.UserNotifications.FirstOrDefault(c =>
c.ForUserID == forUser.ID
&& c.ForObjectTypeID == (short)forObjectType
&& c.ForObjectID == forObjectID
&& c.TypeID == (byte)notificationType
&& c.Date > forUser.NotificationsLastRead.Date));
Note the parameter int? forObjectID.
In query profiler, an example executed SQL statement would be:
exec sp_executesql N'SELECT TOP (1)
[t0].[ID], [t0].[TypeID], [t0].[ForUserID], [t0].[Date], [t0].[ForObjectTypeID], [t0].[ForObjectID], [t0].[Count]
FROM
[dbo].[UserNotifications] AS [t0]
WHERE
([t0].[ForUserID] = #p0)
AND ([t0].[ForObjectTypeID] = #p1)
AND ([t0].[ForObjectID] = #p2)
AND ([t0].[TypeID] = #p3)
AND ([t0].[Date] > #p4)',
N'#p0 int,#p1 int,#p2 int,#p3 int,#p4 datetime',#p0=77812,#p1=5,#p2=NULL,#p3=4,#p4='2018-01-24 13:18:44.107'
When forObjectID is null, the query does not return the expected records. If I change:
AND ([t0].[ForObjectID] = #p2)
To:
AND ([t0].[ForObjectID] IS NULL)
It does return the correct results.
Why is null not handled in the way I would expect it to?
Is there an easy fix? (I can convert the table to not accept nulls for that field and default to 0 but feels icky)
If this is Linq2Sql
change
c.ForObjectID == forObjectID to Object.Equals(c.ForObjectID, forObjectID)
for it to be able to translate to is null when forObjectID is null.
The easy fix would be to write a SQL stored procedure which does what you want -
I doubt that anyone will fix LinkToSql.
Or try this instead:
((c.ForObjectID == forObjectID) ||
(c.ForObjectId == null && forObjectId == null))
Using: Entity Framework 4.3.1, MVC 4
Sample database records (for example):
Id Height
1 null
2 2.1
Why does this first statement bring back zero results:
decimal? scannedItemHeight = default(decimal?);
(from st in Stocks
where st.Height == scannedItemHeight
select st.Id).ToList();
But this statement returns record Id 1:
(from st in Stocks
where st.Height == null
select st.Id).ToList();
If scannedItemHeight is null, I only want to match values where the height is null too.
I only want to return the first record, Id 1.
UPDATE
I ended up using:
st.Height == scannedItemHeight || (scannedItemHeight == null && st.Height == null)
That's because in first case database query would be something like
where Height = #param1 ...
While in second case that would be
where Height is null
First query would return no results because #param1 would be null and no row can match such condition.
Main point is that from C# standpoint those queries are equivalent, but from sql standpoint they are not: you should use IS NULL (or IS NOT NULL) in sql to check for nulls.
How to fix depends on what do you want to do when your parameter is null. In your case: use Jon Skeet's answer.
If someone will want to ignore parameter and not filter by it at all(quite common case, for example when parameter represents user's input in some field, and when nothing is typed there - nothing to filter), then do:
where scannedItemHeight == null || st.Height == scannedItemHeight
which will be in sql like
where #param1 is null OR Height = #param1
I've certainly seen some odd LINQ behaviour before now, due to the way that SQL handles nulls not being the same as the way that C# handles nulls. In C#, a == b is true if a and b are both null, whereas in SQL they're not... instead you have to deliberately check for nullity. I would try:
var temp = (from st in Stocks
where st.Height == scannedItemHeight || (scannedItemHeight == null && st.Height == null)
select st.Id).ToList();
Or construct the queries differently:
var filtered = scannedItemHeight == null
? Stocks.Where(st => st.Height == null)
: Stocks.Where(st => st.Height == scannedItemHeight);
var temp = filtered.Select(st => st.Id).ToList();
I am trying to create a where clause in Linq to SQL using the following logic
if #supplierid is null return all records.
if #supplierid is not null return where supplierid is equals to #supplierid.
and the one that is creating an issue:
if #supplierid ==0 return all records where supplierid is null
I tried writing this like
var answers =
from thisChargeableService in this.GetAll()
where
(
(
(supplierId == null) ||
(
((supplierId < 1) && (thisChargeableService.SupplierId == null)) ||
((supplierId != null) && (thisChargeableService.SupplierId == supplierId.Value))
)
));
This works with the first two conditions but when #supplierid = 0, nothing is returned.
Any help with this would be much appreciated
edit
Basically I have a dropdown of N/A with an id of 0. I have used this to identify that an option has been selected from dropdown and the user is targeting all rows where the supplier id is N/A.
The database contains no entries with 0 as the supplierid, so instead I am trying to target this with where the supplierid is null or the below in SQL
SELECT * FROM ChargeableService
WHERE
(#supplierid is null)
OR
(
(#supplierid is not null and supplierid = #supplierid) or
(#supplierid = 0 AND supplierid is null)
)
With Linq, there is no need to try to build one query to do all. Instead you can build your expression in buts and let deferred execution build and execute the correct sql.
So, this is the way I would do it.
var answers = this.GetAll().AsQueryable();
if (supplierId.HasValue && (supplierId.Value != 0))
answers = answers.Where(a=>a.SupplierId == supplierId.Value);
if (supplierId.HasValue && (supplierId.Value == 0))
answers = answers.Where(a=>!a.SupplierId.HasValue);
I've taken your query and run it against some similar data and the following works:
var answers =
from thisChargeableService in this.GetAll()
where
(
supplierId == null ||
(supplierId == 0 && thisChargeableService.SupplierId == null) ||
(supplierId > 0 && thisChargeableService.SupplierId == supplierId)
)
select thisChargeableService;
I have a simple SQL table which defines a set of hierarchical categories and sub-categories - note the ParentCategoryId can be null for 'top-level' categories...
CREATE TABLE [dbo].[Category](
[CategoryId] [uniqueidentifier] NOT NULL,
[ParentCategoryId] [uniqueidentifier] NULL,
[Name] [nvarchar](50) NOT NULL
)
If I then construct a Linq expression to find a particular category by Name and ParentCategoryId, I find that I cannot get the correct result if I set a Guid? variable to null:
Guid? parentCategoryId = null;
var category = dc.Categories
.Where(c => (
(c.Name == "Fred") &&
(c.ParentCategoryId == parentCategoryId)
));
This does not yield the same result as:
var category = dc.Categories
.Where(c => (
(c.Name == "Fred") &&
(c.ParentCategoryId == null)
));
From what I can find on the web, others have had this problem, but I haven't been able to find a clean workaround to fix the problem.
Any ideas would be much appreciated. Thanks.
Additional Information
Here is the LINQ generated SQL statements for firstly a Guid? null parameter and then for a simple null parameter:
-- With Guid? null parameter : return an empty record set
DECLARE #p0 NVarChar(1000) SET #p0 = 'Fred'
DECLARE #p1 UniqueIdentifier SET #p1 = null
SELECT [t0].[CategoryId], [t0].[ParentCategoryId], [t0].[Name], [t0].[Timestamp]
FROM [dbo].[cad_ScoCategory] AS [t0]
WHERE ([t0].[Name] = #p0) AND ([t0].[ParentCategoryId] = #p1)
-- With null parameter - returns a single (correct) record
DECLARE #p0 NVarChar(1000) SET #p0 = 'Fred'
SELECT [t0].[CategoryId], [t0].[ParentCategoryId], [t0].[Name], [t0].[Timestamp]
FROM [dbo].[cad_ScoCategory] AS [t0]
WHERE ([t0].[Name] = #p0) AND ([t0].[ParentCategoryId] IS NULL)
As you can see, the first option compares the ParentCategoryId with a nulled paraemeter where as the second method checks ParentCategoryId IS NULL - which is correct
No, this is a reasonably common problem unfortunately. The workaround is to explicitly compare with null:
.Where(c => c.Name == Fred &&
((c.ParentCategoryId == parentCategoryId) ||
(c.ParentCategoryId == null && parentCategoryId == null)))
Alternatively, perform the check outside the query (to change which filter is used):
var category = dc.Categories.Where(c => c.Name == "Fred");
category = parentCategoryId == null
? category.Where(c => c.ParentCategoryId == null)
: category.Where(c => c.ParentCategoryId == categoryId);