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/
Related
I want to compare two lists and assign 1st list to another in case of requirement.
var getdetail=_readonlyservice.getdetail().ToList();
foreach(var item in docdetail)
{
var temp=getdetail.firstordefualt(i=>i.Id=item.Id)
if(temp==null) continue;
item.code=temp.code;
}
I want to implement top statements in linq .any help ?
Think so..
var getdetail=_readonlyservice.getdetail().ToList();
var tempList = from dd in context.docdetail
join g in context.getdetail on dd.Id equals g.Id
select new // Your type
{
// Columns...
Code = g.Code
}
I believe you are trying to do like the way I did, although I was going to join table.
var result = (from e in DSE.employees
join d in DSE.departments on e.department_id equals d.department_id
join ws in DSE.workingshifts on e.shift_id equals ws.shift_id
select new
{
FirstName = e.FirstName,
LastName = e.LastName,
Gender = e.Gender,
Salary = e.Salary,
Department_id = e.department_id,
Department_Name = d.department_name,
Shift_id = ws.shift_id,
Duration = ws.duration,
}).ToList();
// TODO utilize the above result
I was using DTO method to do this. And then you return result(as this case is result).
You may view the whole question and solution here.
As this case, you are not required to put foreach loop, as the query said from every row in yourdatabase.table
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.
Given the following database structure
alt text http://dl.dropbox.com/u/26791/tables.png
I'm trying to write a LINQ query that will return images grouped by tags it's associated with. So far I've got this:
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
join t in db.Tags on imgTags.idTag equals t.idTag
where img.OCRData.Contains(searchText.Text)
group img by new { t.TagName } into aGroup
select new
{
GroupName = aGroup.Key.TagName,
Items = from x in aGroup
select new ImageFragment()
{
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};
Which works great. However, I also want to return Images that do not have any tags associated with them in a group of "(Untagged)" or something. I can't wrap my head around how I would do this without inserting a default tag for every image and that seems like generally not a very good solution.
If you want image records when there are no corresponding tag records, you need to perform an outer join on the image tags table.
It's a little tricky, but you can do it in one big query if you have the ability to instantiate new ImageTag and Tag instances for linq to work with. Essentially, when you're doing an outer join, you have to use the into keyword with the DefaultIfEmpty(...) method to deal with the "outer join gaps" (e.g., when the right side of the joined key is null in a typical SQL left outer join).
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
into outerImageRef
from outerIR in outerImageRef.DefaultIfEmpty(new ImageTag() { idImage = img.idImage, idTag = -1 })
join t in db.Tags on imgTags.idTag equals t.idTag
into outerRefTags
from outerRT in outerRefTags.DefaultIfEmpty(new Tag(){ idTag=-1, TagName ="untagged"})
group img by outerRT.TagName into aGroup
select new {
GroupName = aGroup.Key,
Items = from x in aGroup
select new ImageFragment() {
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};
Hopefully the above compiles since I don't have your exact environment, I built my solution using my own data types and then converted it to your question's description. Basically the key parts are the extra into and DefaultIfEmpty lines that essentially help add the extra "rows" into the massively joined table that's in memory if you're thinking about it in the traditional sql sense.
However, there's a more readable solution that doesn't require the in memory instantiation of linq entities (you'll have to convert this one yourself to your environment):
//this first query will return a collection of anonymous types with TagName and ImageId,
// essentially a relation from joining your ImageTags x-ref table and Tags so that
// each row is the tag and image id (as Robert Harvey mentioned in his comment to your Q)
var tagNamesWithImageIds = from tag in Tags
join refer in ImageTags on tag.IdTag equals refer.IdTag
select new {
TagName = tag.Name,
ImageId = refer.IdImage
};
//Now we can get your solution by outer joining the images to the above relation
// and filling in the "outer join gaps" with the anonymous type again of "untagged"
// and then joining that with the Images table one last time to get your grouping and projection.
var images = from img in Images
join t in tagNamesWithImageIds on img.IdImage equals t.ImageId
into outerJoin
from o in outerJoin.DefaultIfEmpty(new { TagName = "untagged", ImageId = img.IdImage })
join img2 in Images on o.ImageId equals img2.IdImage
group img2 by o.TagName into aGroup
select new {
TagName = aGroup.Key,
Images = aGroup.Select(i => i.Data).ToList() //you'll definitely need to replace this with your code's logic. I just had a much simpler data type in my workspace.
};
Hope that makes sense.
Of course, you can always just set your application to tag everything by default w/ "untagged" or do some much simpler LINQ queries to create a list of image id's that are not present in your ImageTag table, and then union or something.
Here's what I ended up doing. I haven't actually checked what kind of SQL this is generating yet, I'm guessing that it's probably not exactly pretty. I think I'd be better off doing a couple queries and aggregating the stuff myself, but in any case this works:
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage into g
from imgTags in g.DefaultIfEmpty()
join t in db.Tags on imgTags.idTag equals t.idTag into g1
from t in g1.DefaultIfEmpty()
where img.OCRData.Contains(searchText.Text)
group img by t == null ? "(No Tags)" : t.TagName into aGroup
select new
{
GroupName = aGroup.Key,
Items = from x in aGroup
select new ImageFragment()
{
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};
With the query below I am doing multiple joins and selecting them all. I want to return this result into a list, so in this case I would have a List with a count of three, assuming that there are three addresses associated with this single customer id order...Right now the query works but I put exp.ToList() it gives me essentially a 2d list ( a list with a single element in which this element is a type list of 3 elements. I'm sure there's a good way to do this... thoughts ?
var exp = (
from t in this.reposOrders.All()
join p1 in this.reposAddress.All()
on t.AddressPrimary equals p1.AddressID into pp1
from p1 in pp1.DefaultIfEmpty()
join p2 in this.reposAddress.All()
on t.AddressSecondary equals p2.AddressID into pp2
from p2 in pp2.DefaultIfEmpty()
join p3 in this.reposAddress.All()
on t.AddressThird equals p3.AddressID into pp3
from p3 in pp3.DefaultIfEmpty()
where t.CustomerID == customerID
select new { p1, p2, p3 }
);
Can you use SelectMany? There are many linq samples here http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
What you want to do suffers from poor normalization; AddressPrimary, AddressSecondary, and AddressThird should not be fields of Orders (I'm assuming order from your line from t in this.reposOrders.All()), but should be contained in a weak entity or join table.
That said, you can probably get what you want with the following query:
var primary = from t in this.reposOrders.All() where t.CustomerID == customerID select t.AddressPrimary;
var secondary = from t in this.reposOrders.All() where t.CustomerID == customerID select t.AddressSecondary;
var tertiary = from t in this.reposOrders.All() where t.CustomerID == customerID select t.AddressThird;
var ids = primary.Union(secondary).Union(tertiary);
var addresses = from a in this.reposAddress.All() where ids.Contains(a.AddressID) select a;
I asked a question earlier about why left joins in Linq can't use defined relationships; to date I haven't got a satisfactory response.
Now, on a parallel track, I've accepted that I need to use the join keyword as if there were no relationship defined between my objects, and I'm trying to work out how to express my query in Linq. Trouble is, it's a conglomeration of left joins between multiple tables, with multiple fields involved in the join. There's no way of simplifying this, so here's the SQL in all its unmasked glory:
select *
from TreatmentPlan tp
join TreatmentPlanDetail tpd on tpd.TreatmentPlanID = tp.ID
join TreatmentAuthorization auth on auth.TreatmentPlanDetailID = tpd.ID
left join PatientServicePrescription rx on tpd.ServiceTypeID = rx.ServiceTypeID
left join PayerServiceTypeRules pstr on auth.PayerID = pstr.PayerID and tpd.ServiceTypeID = pstr.ServiceTypeID and pstr.RequiresPrescription = 1
where tp.PatientID = #PatientID
(FYI, if it helps to understand what I'm trying to do: I'm trying to identify if there are any TreatmentPlanDetail records for this Patient where the authorizing Payer requires a prescription for this ServiceType, but there is either no ServicePerscription record, or it has expired.)
Now, here's what my C# code looks like:
var q = from tp in TreatmentPlans
from tpd in tp.Details
from auth in tpd.Authorizations
join rx in ServicePrescriptions.DefaultIfEmpty() on tpd.ServiceTypeID equals rx.ServiceTypeID
// from pstr in auth.Payer.ServiceTypeRules.DefaultIfEmpty() -- very frustrating that this doesn't work!!
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
on new { auth.PayerID, tpd.ServiceTypeID, RxReq = (bool)true } equals new { pstr.PayerID, pstr.ServiceTypeID, pstr.RequiresPrescription }
select new { Payer = auth.Payer, Prescription = rx, TreatmentPlanDetail = tpd, Rules = pstr };
Oops, doesn't compile! For some reason (I'd love an explanation) I can't use that literal boolean inside the equijoin! Fine, I'll leave it out, and filter out the "RequiresPrescription" stuff later...
...
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
on new { auth.PayerID, tpd.ServiceTypeID } equals new { pstr.PayerID, pstr.ServiceTypeID }
...
... and now it compiles - but when I run, I get an "Object reference not set" exception on this line. DUH! Of course there's a null in there! How else are you supposed to perform a comparison with a left join, if you're not allowed to reference the object on the right side, that might potentially be null?
So, how are you supposed to do a left join using multiple fields?
I think you need to use the into keyword and resolve the missing children's DefaultIfEmpty() after the join, not before:
...
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>()
on new { auth.PayerID, tpd.ServiceTypeID, bool RequiresPrescription = true }
equals new { pstr.PayerID, pstr.ServiceTypeID, pstr.RequiresPrescription }
into pstrs
from PSTR in pstrs.DefaultIfEmpty()
select new {
Payer = auth.Payer,
Prescription = rx,
TreatmentPlanDetail = tpd,
Rules = PSTR
};
LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty() is probably turning up a null because the DataTable returned contains no rows, thus causing your exception. Note the entire statement after in will be executed before selecting into it, which is not your desired behavior. You want the matching rows or null if no matching rows exist.
For the boolean problem, it is a naming problem (nothing matches "RxReq" on the right side and nothing matches "RequiresPrescription" on the left side). Try naming the true "RequiresPrescription" as I have above (or name the right side's pstr.RequiresPrescription "RxReq").