DefaultIfEmpty() in LINQ to SQL join causing duplicates - c#

Code:
var cons = from c in dc.Consignments
join p in dc.PODs ON c.ID equals p.Consignment into pg
from p in pg.DefaultIfEmpty()
...(other joins)...
select new {
...
PODs = pg
...
}
Basically, I want one row to be selected for each Consignment, and I want to select the object 'PODs' which should be a collection of PODs. This works, however I get an row for each POD there is - so if I have 3 POD's on a consignment, 3 rows will be returned for that consignment. Am I selecting the PODs incorrectly? If I take away the DefaultIfEmpty(), it strangely works fine and doesn't cause duplicates.

You're using a second from clause, which is effectively flattening things - but then you're still using pg in your select. The point of DefaultIfEmpty() is if you want a left outer join, effectively - where you would expect one result per valid combination.
I suspect you just want:
var cons = from c in dc.Consignments
join p in dc.PODs ON c.ID equals p.Consignment into pg
select new {
...
PODs = pg
...
}
or maybe
var cons = from c in dc.Consignments
join p in dc.PODs ON c.ID equals p.Consignment into pg
select new {
...
PODs = pg.DefaultIfEmpty()
...
}
... but the latter will give you a result with a single null entry in PODs when there weren't any PODs, which probably isn't what you were after.

Related

Left join with where clause in linq

I am trying to do a left join with a where clause in linq.
I have leadsQuery table with 2500 rows. I want to join the LeadCons table into it. For a lead there can be multiple entries in the LeadCons table, hence I want to join only when the Status match. Else I want the fields to be NULL.
var data = from lead in leadsQuery
join lcs in context.LeadCons on lead.ID equals lcs.LeadId into leadsWithCons
from lcs in leadsWithCons.DefaultIfEmpty()
where lead.Status == lcs.Status
select new
{
LeadId = lead.ID,
Source = lead.Source.ToString(),
};
This query gives me ~1500 rows and leadsQuery has 2500. What am I doing wrong here?
A late answer, hoping it is still helpful:
First, you aren't selecting any values from LeadCons, so what is the purpose of a join?
I shall assume maybe you want to extend your select, so let us say you want to select the property foo, so my next question: Why do you need a left join in your case? You can simply do a select:
var data = from lead in leadsQuery
select new
{
Foo = context.LeadCons.Where(lcs => lead.Status == lcs.Status).SingleOrDefault().foo
LeadId = lead.ID,
Source = lead.Source.ToString(),
};
This way you have the same number of items and for each item the desired foo value.
Have you tried just changing the your join to a join with multiple conditions, and then removing the where 'status equal status'
from lead in leadsQuery
join lcs in context.LeadCons on new {
p1 = lead.ID,
p2 = lead.Status
}
equals
new {
p1 = lcs.LeadId,
p2 = lcs.Status
}
you can have a look at this nice article:
https://smehrozalam.wordpress.com/2010/04/13/linq-how-to-write-queries-with-complex-join-conditions/

EF core 2.1 select/distinct

I'm using sql profiler to see sql generated by Ef core2.1,
this is my linq query :
var resulat = (from a in A
join b in B equals a.level=b.level
where ...
select new M1 {AId = a.id}).Distinct();
(from r in resulat
join c in C equals r.AId = c.AId
select new M2
{
CId = c.Id,
level = _helper(c.level)
}).Distinct();
Sql generated:
select t.AId,c.Id,c.level
from
(
select distinct a.id
from A a
inner join B b on a.level=b.level
where ...
) as t
inner join C c on t.AId = c.AId
What i want as result is :
select distinct c.Id,c.level
from
(
select distinct a.id
from A a
inner join B b on a.level=b.level
where ...
) as t
inner join C c on t.AId = c.AId
I have tried also using select/distinct with result IQueryable, but the sql generated is the same.
what i missed in my linq query or what i have to add to have this sql query
That's what worked for me:
Delete Distinct() from result query, this avoid adding t.AId to my selection.
Delete a helper method from one of my selection fields performe adding Distinct() to final query.
This is my query after correction:
var resulat = from a in A
join b in B equals a.level=b.level
where ...
select new M1 {AId = a.id};
(from r in resulat
join c in C equals r.AId = c.AId
select new M2
{
CId = c.Id
level = c.level
}).Distinct();
Many thanks for your comments, it really helped me.
I'm always a fan of querying the data you want directly from the table (well, DbSet) that returns the data. The process looks a bit like these steps:
I want C.Id and C.Level
That's context.Cs.
Which Cs do I want?
The ones that have a parent A, of which at least one B has the same 'level' as A and meets a couple of other criteria (the where ...).
That amounts to:
from c in context.Cs
where context.Bs.Any(b => b.level == c.A.level && <other criteria>)
select new { c.Id, c.Level }
If the where ... also contains filter criteria for A you can add predicates like && c.A == ... to the where.
Note that I assume a navigation property c.A to be present, otherwise to be created, because C has AId.

getting error in joining dictionary with table

I want to have join query from a table with a dictionary based on a common field, my query is:
var query = from c in db.Exp
join d in lstUniprotDic on c.UniID equals d.Key
select new
{
c.UniID,
IdentityPercent=d.Value.ToString(),
c.PrId,
c.SpotNo
}
but i got the following error
Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
That pretty much says it all. You can't use the dictionary in your LINQ to SQL query except when using Contains.
Solution:
(from c in db.Exp where lstUniprotDic.Keys.Contains(c.UniID) select c).AsEnumerable()
join d in lstUniprotDic on c.UniID equals d.Key
select new
{
c.UniID,
IdentityPercent=d.Value.ToString(),
c.PrId,
c.SpotNo
}
I am not sure if the usage of lstUniprotDic.Keys in the LINQ to SQL query is actually working.
If not, try using this code instead:
var ids = lstUniprotDic.Keys.ToArray();
(from c in db.Exp where ids.Contains(c.UniID) select c).AsEnumerable()
join d in lstUniprotDic on c.UniID equals d.Key
select new
{
c.UniID,
IdentityPercent=d.Value.ToString(),
c.PrId,
c.SpotNo
}

LINQ to Entites join on ID or null

I have a LINQ to Entities statement that joins two models on an AlphaGroupID, like this:
IEnumerable<ICD.ViewModels.HomeSearchViewModel> ICDList = (from a in ICDUnitOfWork.AlphaGroups.Find()
join e in ICDUnitOfWork.Alphas.Find()
on a.AlphaGroupID equals e.AlphaGroupID)
I need to join the two tables on AlphaGroupID, but I also need all of the ICDUnitOfWork.AlphaGroups regardless of whether or not they have a corresponding AlphaGroupID in ICDList. How can I accomplish this?
Use join into (it is same as GroupJoin):
var query = from a in ICDUnitOfWork.AlphaGroups.Find()
join e in ICDUnitOfWork.Alphas.Find()
on a.AlphaGroupID equals e.AlphaGroupID into g
select new { AlphaGroup = a, Alphas = g };
GroupJoin produces hierarchical result - for each item in outer sequence will be generated sequence of corresponding items in inner sequence (sequence could be empty).

Left Outer Join

List<ServicePacksDTO> allServicePacks = new List<ServicePacksDTO>();
using (var db = new DataContext())
{
allServicePacks=(
from sp in db.ServicePacks
join st in db.States.DefaultIfEmpty() on sp.State_id equals st.State_Id
join type in db.ServiceTypes on sp.ServiceType_Id equals type.ServiceType_Id
where
(type.ServiceType_desc.ToLower() == "accepted")
orderby sp.AustState_id
select sp.ToServicePacksDTO(db)).ToList();
}
The current code works fine, until I attempt to do an outer join on state. Is there away to do this easily?
Well firstly, you need to provide a bit more detail that "works fine, until i attempt to do xx".
What doesn't work? Is there an error? Unexpected results?
Forgetting about the DTO projection and db.ServiceTypes join, to do a LOJ between db.ServicePacks and db.States, do this:
var x = (from sp in db.ServicePacks
join st in db.States on sp.State_id equals st.State_id into spst
from x in spst.DefaultIfEmpty()
select new { /* fields */ }
).ToList();
Try that first, make sure that works (it should), and then add on your other join and projection.

Categories

Resources