Linq NOT EXISTS multiple tables - linq

I want to do a LINQ NOT EXISTS on query MULTIPLE TABLES.
All examples on Google or SO are handling two tables I'm working with three so I'm struggling as a newbie on LINQ on how to reference them correctly.
First I tried this LINQ query
var nocertificates = (
from x in rmdb.t_certificates
from ce in rmdb.t_user_certificates
from u in rmdb.t_users
where u.id == ce.uid && ce.value != x.id
select x).AsEnumerable().Select(x => new ViewModelCheckBox()
{
Value = x.id.ToString(),
Name = x.name,
Checked = false
});
I used the ugly three times from as I'm not that good with creating types for joining.
But that gave wrong result and I realized I had to go for a NOT EXISTS
So I built a new query in T-SQL
This is the SQL query it works!
select distinct * from t_certificates tc
where NOT EXISTS
(
select distinct * from t_users tu, t_user_certificates tuc
WHERE tu.email = 'user#email.com'
and tu.id = tuc.[uid]
and tuc.value = tc.id
)
How would I do that in LINQ?
This is the question, I will award my answer for that!
BUT!
When we are at it... I'm really curious on the answer.. Is it possible to do one LINQ query that return an Ienumerable with both those that EXISTS and NOT EXISTS resulting in an object which will hold DIFFERENT VALUES on the checked property EXISTS -> CHECKED = true NOT EXISTS -> CHECKED = false
This is how I create my object.
.Select(x => new ViewModelCheckBox()
{
Value = x.id.ToString(),
Name = x.name,
Checked = this should be different based on exists or not
});

The LINQ answer should look something like this (untested):
var nocertificates =
from x in rmdb.t_certificates
join tuc in (
from u in rmdb.t_users
join ce in rmdb.t_user_certificates on u.id == ce.uid
select ce.value
) on tuc.value = tc.id into tuc
from y in tuc.DefaultIfEmpty()
where y == null
select x;

This is what I ended up using!
var query = (from tc in rmdb.t_certificates
where !(
from tu in rmdb.t_users
from tuc in rmdb.t_user_certificates
where tu.email == username
&& tu.id == tuc.uid
&& tuc.value == tc.id select tc).AsEnumerable().Any()
select new ViewModelCheckBox()
{ Checked = false,
intconverter = tc.id,
Name = tc.name
});

Related

Left joining a table's id with the maximum id from a subquery

I am using .NET core and trying to write a Linq query to list Cases. I have 3 tables that I am trying to join together:
Cases - Includes CaseID and CaseStatusID
CaseStatuses - Includes CaseStatusID (a Case has one of these)
CaseStatusHistory - Includes CaseStatusHistoryID, CaseID, CaseStatusID (a Case has many of these).
I can join Cases and CaseStatuses easily on CaseStatusID but am not sure how to join CaseStatusHistory on CaseStatusHistoryID = a subquery selecting the MAX(CaseStatusHistoryID) WHERE CaseID and CaseStatusID matches. I could even select the CaseStatusHitory later in my results but am not sure how.
This is what I need to convert
SELECT
c.CaseID,
csh.DateTimeAdded
FROM dbo.Cases c
JOIN dbo.CaseStatuses cs
ON c.CaseStatusID = cs.CaseStatusID
LEFT JOIN dbo.CaseStatusHistory csh
ON csh.CaseStatusHistoryID =
(
SELECT MAX(CaseStatusHistoryID)
FROM dbo.CaseStatusHistory
WHERE CaseID = c.CaseID
AND cs.CaseStatusID = c.CaseStatusID
)
This is what I have so far in Linq
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
CaseStatus = cs.CaseStatus
//StatusChangeDate = csh.DateTimeAdded
};
I need to add something like this
From csh In CaseStatusHistories
Where
(
From _csh In CaseStatusHistories
Where _csh.CaseID = c.CaseID AndAlso _csh.CaseStatusID = c.CaseStatusID
Select _csh.CaseStatusHistoryID
).Max = csh.CaseStatusHistoryID
Here is what I have so far but it does not return any result - just seems to time out.
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
from rg in _db.RepairingGarages.Where(rg => rg.RepairingGarageId == c.RepairingGarageId.Value).DefaultIfEmpty()
from rgb in _db.Businesses.Where(rgb => rgb.BusinessId == rg.BusinessId.Value).DefaultIfEmpty()
from ec in _db.EngineeringCompanies.Where(ec => ec.EngineeringCompanyId == c.EngineeringCompanyId.Value).DefaultIfEmpty()
from ecb in _db.Businesses.Where(ecb => ecb.BusinessId == ec.BusinessId.Value).DefaultIfEmpty()
from csh in _db.CaseStatusHistory.Where(csh => csh.CaseStatusHistoryId ==
(
from _csh in _db.CaseStatusHistory
where _csh.CaseId == c.CaseId && _csh.CaseStatusId == c.CaseStatusId.Value
select _csh.CaseStatusHistoryId
).Max()).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
RepairingGarage = rgb.BusinessName,
Engineer = ecb.BusinessName,
CaseStatus = cs.CaseStatus,
StatusChangeDate = csh.DateTimeAdded
};
I just need to get the StatusChangeDate which is the DateTimeAdded in the CaseStatusHistory table. Cases can be at the same status more than once, so I just need the DateTimeAdded with the highest CaseStatusHistoryID for a particular Case. Thank you very much for any help.
This appears to answer my question
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
from rg in _db.RepairingGarages.Where(rg => rg.RepairingGarageId == c.RepairingGarageId.Value).DefaultIfEmpty()
from rgb in _db.Businesses.Where(rgb => rgb.BusinessId == rg.BusinessId.Value).DefaultIfEmpty()
from ec in _db.EngineeringCompanies.Where(ec => ec.EngineeringCompanyId == c.EngineeringCompanyId.Value).DefaultIfEmpty()
from ecb in _db.Businesses.Where(ecb => ecb.BusinessId == ec.BusinessId.Value).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
RepairingGarage = rgb.BusinessName,
Engineer = ecb.BusinessName,
CaseStatus = cs.CaseStatus,
StatusChangeDate =
(
from _csh in _db.CaseStatusHistory
where _csh.CaseId == c.CaseId && _csh.CaseStatusId == cs.CaseStatusId
orderby _csh.CaseStatusHistoryId descending
select _csh.DateTimeAdded
).FirstOrDefault()
};
The only trouble with not joining is that I am not sure if I can sort by this date now. Any better ideas? Thank you.

EF Linq query with conditional include

So I have the following Linq query:
var member = (from mem in
context.Members.Include(m =>
m.MemberProjects.Select(mp => mp.Project))
where mem.MemberId == memberId
select mem).FirstOrDefault();
This returns a Member entity, with a set of MemberProjects that have a Project child. I would like to limit the MemberProjects to only those for which the Project child has a property
ProjectIdParent == null.
One of my failed attempts might make the intent clearer:
var member = (from mem in context.Members
.Include(m => m.MemberProjects
.Where(mp =>
mp.Project.ProjectIdParent == null)
.Select(proj => proj.Project))
where mem.MemberId == memberId
select mem).FirstOrDefault();
This of course complains of an invalid Include expression because of the Where clause.
Any thoughts on how to do this would be great :)
DISCLAIMER: I havent tested this. This is just an idea. If you let me know the results, I will update this accordingly. (Skip to the update part for the tested solutions)
var member = (from mps in context.MemberProjects
.Include(m => m.Members)
.Include(m => m.Projects)
where mps.Project.ProjectIdParent == null
select mps)
.FirstOrDefault(mprojs => mprojs.Member.MemberId == memberId);
I'd also analyze the queries using something like EFProfiler to make sure the generated queries dont leave the realm of sanity.
You can also take a look at this post by Jimmy Bogard on Many to Many relationships with ORMs.
Update
I came up with multiple tested solutions for this with EF 6.1.3. My Edmx looked like below:
The setup data is like below:
I was able to run code below to get the MemberFive correctly
var member = context.Members.FirstOrDefault
(m => m.MemberId == memberId
&& m.Projects.Any(p => p.ProjectParentId == null));
The generated SQL looked like this:
SELECT TOP (1) [Extent1].[MemberId] AS [MemberId],
[Extent1].[MemberName] AS [MemberName]
FROM [dbo].[Members] AS [Extent1]
WHERE ([Extent1].[MemberId] = 1)
AND (EXISTS (SELECT 1 AS [C1]
FROM (SELECT [MemberProjects].[MemberId] AS [MemberId],
[MemberProjects].[ProjectId] AS [ProjectId]
FROM [dbo].[MemberProjects] AS [MemberProjects])
AS [Extent2]
INNER JOIN [dbo].[Projects] AS [Extent3]
ON [Extent3].[ProjectId] = [Extent2].[ProjectId]
WHERE ([Extent1].[MemberId] = [Extent2].[MemberId])
AND ([Extent3].[ProjectParentId] IS NULL)))
If you dont like the generated query you can use this:
var memberQuery = #"Select M.* from Members M
inner join MemberProjects MP on M.MemberId = Mp.ProjectId
inner join Projects P on MP.ProjectId = P.ProjectId
where M.MemberId = #MemberId and P.ProjectParentId is NULL";
var memberParams = new[]
{
new SqlParameter("#MemberId", 1)
};
var member3 = context.Members.SqlQuery(memberQuery, memberParams)
.FirstOrDefault();
The later consistently returned under 20ms vs the other one hovered around 60ms (if that matters to you).
I hope this helps.

How to find Distinct in more than one column in LINQ

I have a LINQ statement that returns many columns. I need to find distinct of unique combination of two columns. What is the best way to do this.
var productAttributeQuery =
from pa in ctx.exch_productattributeSet
join pp in ctx.exch_parentproductSet
on pa.exch_ParentProductId.Id equals pp.Id
join ep in ctx.exch_exchangeproductSet
on pp.exch_parentproductId equals ep.exch_ParentProductId.Id
where pa.exch_EffBeginDate <= effectiveDateForBeginCompare
&& pa.exch_EffEndDate >= effectiveDateForEndCompare
&& pa.statuscode == StusCodeEnum.Active
where pp.exch_EffBeginDate <= effectiveDateForBeginCompare
&& pp.exch_EffEndDate >= effectiveDateForEndCompare
&& pp.statuscode == StatusCodeEnum.Active
where ep.statuscode == StatusCodeEnum.Active
select new ProductAttributeDto
{
ParentProductId = pa.exch_ParentProductId.Id,
AttributeId = pa.exch_AttributeId.Id,
AttributeValue = pa.exch_Value,
AttributeRawValue = pa.exch_RawValue
};
return productAttributeQuery.ToList();
I want to get Distinct combination of ParentProductId and AttributeId from this list
You can group by anonymous type and select keys (they will be distinct)
var query = from p in productAttributeQuery
group p by new {
p.ParentProductId,
p.AttributeId
} into g
select g.Key;
You can use same approach with you original query if you want to get distinct pairs on server side.
Another approach - project results into pairs and get distinct from them:
var query = productAttributeQuery
.Select(p => new { p.ParentProductId, p.AttributeId })
.Distinct();

In join want to get not matched records

Work on DF 4 vs 2010.Face problem on join on SalSalesOrderDetail with SalSalesOrderFinancial table.
In SalSalesOrderFinancial one record have SalesOrderDetailID=null. Want to get those records whose SalesOrderDetailID is not present in SalSalesOrderFinancial.
To get desired out put write bellow linq syntax,it’s working .Looking for better join syntax .Is there any way to get desired in one join.
var tempBDwithSODetail = (from p in this.Context.SalSalesOrderFinancials
where p.SalesOrderDetailID != null
select p.SalesOrderDetailID).AsEnumerable();
var tempBDwithOutSODetail = (from p in this.Context.SalSalesOrderFinancials where p.SalesOrderDetailID == null select p).AsEnumerable();
var querySOD = (this.Context.SalSalesOrderDetails.Where(item => !tempBDwithSODetail.Contains(item.SalesOrderDetailID))).AsEnumerable();
var tempBDetail = (from p in querySOD
join q in tempBDwithOutSODetail on p.SalesOrderID equals q.SalesOrderID
where q.SalesOrderDetailID == null
select new
{
q.SalesOrderID,
p.SalesOrderDetailID,
q.CurrencyID,
q.BillingPolicyID,
q.BillTypeID,
q.BillingTypeID
}).AsEnumerable();
If have any query please ask.Thanks in advanced.
if i got you, you just need to simple left join between SalSalesOrderDetail and SalSalesOrderFinancial...
var query = (from u in this.Context.SalSalesOrderDetail
join t in this.Context.SalSalesOrderFinancials
on u.SalesOrderDetailID equals t.SalesOrderDetailID into JoinedList
from t in JoinedList.DefaultIfEmpty()
select new
{
SalSalesOrderDetail = t,
SalSalesOrderFinancials = u
})
.Where(u => u.SalSalesOrderFinancials == null)
.ToList();

LINQ to Entities three table join query

I'm having a bit trouble with a query in Linq to Entities which I hope someone can shed a light on :-) What I'm trying to do is to create a query that joins three tables.
So far it works, but since the last table I'm trying to join is empty, the result of the query doesn't contain any records. When I remove the last join, it gives me the right results.
My query looks like this:
var query = from p in db.QuizParticipants
join points in db.ParticipantPoints on p.id
equals points.participantId into participantsGroup
from po in participantsGroup
join winners in db.Winners on p.id
equals winners.participantId into winnersGroup
from w in winnersGroup
where p.hasAttended == 1 && p.weeknumber == weeknumber
select new
{
ParticipantId = p.id,
HasAttended = p.hasAttended,
Weeknumber = p.weeknumber,
UmbracoMemberId = p.umbMemberId,
Points = po.points,
HasWonFirstPrize = w.hasWonFirstPrize,
HasWonVoucher = w.hasWonVoucher
};
What I would like is to get some records even if the Winners table is empty or there is no match in it.
Any help/hint on this is greatly appreciated! :-)
Thanks a lot in advance.
/ Bo
If you set these up as related entities instead of doing joins, I think it will be easier to do what you're trying to do.
var query = from p in db.QuizParticipants
where p.hasAttended == 1 && p.weeknumber == weeknumber
select new
{
ParticipantId = p.id,
HasAttended = p.hasAttended,
Weeknumber = p.weeknumber,
UmbracoMemberId = p.umbMemberId,
Points = p.ParticipantPoints.Sum(pts => pts.points),
HasWonFirstPrize = p.Winners.Any(w => w.hasWonFirstPrize),
HasWonVoucher = p.Winners.Any(w => w.hasWonVoucher)
};
This is assuming hasWonFirstPrize and hasWonVoucher are boolean fields, but you can use any aggregate function to get the results you need, such as p.Winners.Any(w => w.hasWonFirstPrize == 1)
I don't use query syntax a lot but I believe you need to change from w in winnersGroup to from w in winnersGroup.DefaultIfEmpty()

Resources