How to Convert SQL query consisting 'not in' into LINQ? - entity-framework

SELECT distinct [Taskid] FROM[Dbimg].[dbo].[table1] where Taskid not in (select Taskid from[Dbimg].[dbo].[table1] where ValidationFlag is null)
Please help to convert above sql query in linq c#.

You can try the below code
db.table1.Where(x => !db.table1.Where(y => y.ValidationFlag == null).Select(z => z.Taskid).Contains(x.TaskId))
.Select(x => x.TaskId).Distinct();
But in your case you can also modify the above LINQ as below as you are using the same table
db.table1.Where(y => y.ValidationFlag != null).Select(z => z.Taskid).Distinct();

Related

Performing a subquery in Entity Framework Core

I'm trying to convert some common queries that we use to an API call using EF Core but I can't seem to get it working.
The SQL that will typically be run will looking something like this:
select *
from my_table
where convert(date, datefield) = (select date
from another_table
where name == 'my_string')
My attempts at converting this into Linq have been fruitless. I've tried something like this and similar variants.
public async Task<my_table> GetStuff()
{
return await _context.my_table
.Where(m => _context.another_table
.Where(a => a.name == "my_string")
.Select(a => a.date).Equals(m.updatedate)).FirstAsync();
}
There error I get is:
System.InvalidOperationException: The LINQ expression 'DbSet<my_table>()
.Where(t => DbSet<another_table>()
.Where(t0 => t0.name == "my_string")
.Select(t0 => t0.date).Equals((object)t.updatedate))' could not be translated.
I've seen some posts about using Include or joining, but these tables are not related.
Any help would be appreciated. Thanks!
The first thing to mention is that even SQL allows the following
where convert(date, datefield) = (select date
from another_table
where name == 'my_string')
it will fail if the right subquery returns more than one result. Because in general subqueries (so is the LINQ "collections") return sequences, not single value.
So the safer and better approach would be to use either IN
where convert(date, datefield) in (select date
from another_table
where name == 'my_string')
or EXISTS
where exists (select * from another_table
where name == 'my_string' and date == convert(date, my_table.datefield))
Both these translate naturally to LINQ - IN to Contains
_context.my_table
.Where(m => _context.another_table
.Where(a => a.name == "my_string")
.Select(a => a.date)
.Contains(m.updatedate) // <--
)
and EXISTS to Any
_context.my_table
.Where(m => _context.another_table
.Any(a => a.name == "my_string" && a.date == m.updatedate) // <--
)

EF Core 5.0 where predicate is ignored when in stored in variable

after update to EF Core 5.0 I've notice long running query.
Maybe I found EF Core 5.0 bug, but maybe it's a breaking change I didn't found here https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/breaking-changes.
I have a function, that takes "where" predicate as a parameter, so this is something I cannot easily rewrite.
Code that works:
var reps = ctx.Reports.AsNoTracking()
.Include(r => r.ReportColumns)
.Where(r => r.Name == "ListOfCustomers")
.ToList();
and generates correct query (WHERE clause inluded):
SELECT [r].[ReportID], [r].[AdvancedFilterPurpose] -- ommited columns
FROM [report].[Reports] AS [r]
LEFT JOIN [report].[ReportColumns] AS [r0] ON [r].[ReportID] = [r0].[ReportID]
WHERE [r].[Name] = N'ListOfCustomers'
ORDER BY [r].[ReportID], [r0].[ReportColumnID]
Code that doesn't work:
Func<Db.Model.Report, bool> a = (r) => r.Name == "ListOfCustomers";
var reps = ctx.Reports.AsNoTracking()
.Include(r => r.ReportColumns)
.Where(a)
.ToList();
and generates query without WHERE clause
SELECT [r].[ReportID], [r].[AdvancedFilterPurpose] -- ommited columns
FROM [report].[Reports] AS [r]
LEFT JOIN [report].[ReportColumns] AS [r0] ON [r].[ReportID] = [r0].[ReportID]
ORDER BY [r].[ReportID], [r0].[ReportColumnID]
Thanks for any suggestion.
Replace type Func<Db.Model.Report, bool> by Expression<Func<Db.Model.Report,bool>>.
This won't work: Func<Db.Model.Report, bool> a = (r) => r.Name == "ListOfCustomers"
This will work: Expression<Func<Db.Model.Report, bool>> a = (r) => r.Name == "ListOfCustomers"

Linq to Entities using Lambda Expressions and multiple where conditions

I am trying to select a list of objects at the end of a fairly long chain of joins/selects using Linq to Entities written as Lambda Expressions... Here is what I have currently the following two statements.
var formDefId = _unitOfWork.AsQueryableFor<FormTrack>()
.Where(x => x.FormTrackId == formTrackId)
.Select(x => x.FormDefId).First();
var rules = _unitOfWork.AsQueryableFor<FormTrack>()
.Where(x => x.FormTrackId == formTrackId)
.Select(x => x.FormDef)
.SelectMany(x => x.Events
.Where(y => y.EventTypeId == 7))
.Select(x => x.RuleGroup)
.SelectMany(x => x.Rules)
.SelectMany(x => x.RuleFormXmls
.Where(y => y.FormDefId == formDefId));
What I would like to do, is combine the two queries, and use the FormDefId returned by
.Select(x => x.FormDef)
in the final where clause instead of having to use the formDefId from a separate query.
Is this something that is possible?
Thank you in advance for your help
It is much easier to write this using query syntax.. Each from in query syntax corresponds to a SelectMany in lambda syntax. This allows you to have all the variables in scope.
var rules =
from ft in _unitOfWork.AsQueryableFor<FormTrack>()
from e in ft.FormDef.Events
from r in e.RuleGroup.Rules
from x in r.RuleFormXmls
where ft.FormTrackId == formTrackId
where e.EventTypeId == 7
where x.FormDefId == ft.FormDefId
select x

Entity Framework LINQ Query match all members of child collection

I have a Site table that has a many-to-many relationship with a UtilityServiceConnection table using a linking table called LinkSiteUtilityServiceConnection. Given a set of ServiceConnectionIds, I need to locate the Site that is exclusively linked to all of them and no more. I think I should be able to write the query using All on the collection but it's not working as expected.
var serviceConnectionIds = new[] { 546892, 546911, 546923 };
var sites1 = db.Sites
.Where(x => x.LinkSiteUtilityServiceConnections.All(y => serviceConnectionIds.Contains(y.UtilityServiceConnectionId)))
.ToList();
Assert.AreEqual(1, sites1.Count); //fails
This produces the query below that returns ~250,000 records when I expect to get one.
SELECT [Extent1].*
FROM [dbo].[Site] AS [Extent1]
WHERE NOT EXISTS (SELECT 1 AS [C1]
FROM [dbo].[LinkSiteUtilityServiceConnection] AS [Extent2]
WHERE ([Extent1].[SiteId] = [Extent2].[SiteId])
AND ((NOT ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923)))
OR (CASE
WHEN ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923)) THEN cast(1 as bit)
WHEN (NOT ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923))) THEN cast(0 as bit)
END IS NULL)))
Why isn't All working as I expect? What's the best way to write this query?
check this code:
query 1:
var sites1 = db.Sites
.Where(x => serviceConnectionIds.All(y =>
x.LinkSiteUtilityServiceConnections
.Select(u => u.UtilityServiceConnectionId).Contains(y)))
.ToList();
query 2:
var query = db.Posts.AsQueryable();
var sites1 = serviceConnectionIds.Aggregate(query,
(current, item) => current.Where(e => e.LinkSiteUtilityServiceConnections
.Any(c => c.UtilityServiceConnectionId== item))).ToList();

Why are anonymous Types in EF4 different from LINQ to SQL ones?

I have the following query in LINQ to SQL to fetch all records from a Table that are not already in a jointable.
// <param name="id">The ID of the Person</param>
IEnumberable<object> GetUnassignedClients(int id)
{
_db.Clients
.Select(i => new
{
Client_id = i.Id,
Person_id = id,
Cid = id + "." + i.Id // Please don't ask why I do this. I just have to do it
// ... some more fields
})
.Where(o =>
!_db.Clients_Persons
.Where(t => t.Person_id == id)
.Select(t => t.Client_id)
.Contains(o.Client_id))
.Distinct().ToList();
}
Now I have started a migration to EF4 but the "Cid" part of the anonymous type with the combination ToList() (ToList() triggered the exception is a simplified testcase without the WHERE condition) fails with the exception:
Unable to create a constant value of
type 'System.Object'. Only primitive
types ('such as Int32, String, and
Guid') are supported in this context.
Why is that so or am I missing something here?
EF does not know how to translate the expression id + "." + i.Id into valid SQL which is why it fails. You have to tell EF that it needs to convert id from an integer to a string. You can do this using the SqlFunctions class in the following way:
var ret = _db.Clients
.Select(i => new
{
Client_id = i.Id,
Person_id = id,
Cid = SqlFunctions.StringConvert((double) id) + "." + SqlFunctions.StringConvert((double) i.Id) // Please don't ask why I do this. I just have to do it
// ... some more fields
})
.Where(o =>
!_db.Clients_Persons
.Where(t => t.Person_id == id)
.Select(t => t.Client_id)
.Contains(id)
)
.Distinct()
.ToList()
;