How to combine 2 LINQ into one - linq

I am trying to populate a treeview control with some info and for that I am using 2 separate LINQ statements as follows;
Dim a = From i In CustomerTable _
Group By ShipToCustName = i.Item(6), BillToCustName = i.Item(4) Into Details = Group, Count() _
Order By ShipToCustName
Dim b = From x In a _
Group By ShipToName = x.ShipToCustName Into Group, Count() _
Order By ShipToName
For Each item In b
Console.WriteLine("Shitp To Location: " + item.ShipToName)
For Each x In item.Group
Console.WriteLine("...BillTo:" + x.BillToCustName)
For Each ticket In x.Details
Console.WriteLine("......TicketNum" + ticket.Item(0).ToString)
Next
Next
Next
Is there a way to combine A and B into one query ? Any help please ...

Well, really B is a single query. Remember that what comes back from a Linq statement is an IQueryable, not the actual result. This means that B combines its own expressions with that of A, but retrieval is not performed until you enumerate B. So B really is just a single query.

Related

LINQ Query Variable passed to Another LINQ Query

What is the difference between the query in this post:
Save LINQ Query As Variable To Call Another LINQ Query
var parentLoc = (from a in db.PartsLocations
where a.LocationName == aContainer
select a.ParentLocation);
var locations = (from b in db.PartsLocations
where b.LocationID == parentLoc
select b).ToList();
to the following example from this post.
Dim persVogel = From p In db.People
Where p.LastName = "Vogel"
Select p
Dim persVogelPHVIS = From pp In persVogel
Where pp.Company.Name = "PHVIS"
Select pp
Both of these have declared 2 queries and use the first query variable into second query.
What is the reason to use Single() in the first example but was not used in visualstudiomagazine.com article? Thanks
The two samples are fundamentally different.
First Sample
I think you pasted this one incorrectly...from the referenced question, the query should be:
var parentLoc = (from a in db.PartsLocations
where a.LocationName == aContainer
select a.ParentLocation).Single();
var locations = (from b in db.PartsLocations
where b.LocationID == parentLoc
select b).ToList();
(I am going to assume that LocationID and ParentLocation are typed as int.)
In this sample parentLoc is an int - a single instance of a ParentLocation value, obtained from the PartsLocations table. So what you get is an int.
The second linq statement sources its records also from the PartsLocations table. It uses parentLoc to identify records within that table (e.g. where b.LocationID == parentLoc). What you get at the end is a set of PartsLocations records.
The .Single() call is made because you want to compare the result to LocationID in the second statement, and cannot compare int to IEnumerable<int>.
Second Sample
Dim persVogel = From p In db.People
Where p.LastName = "Vogel"
Select p
Dim persVogelPHVIS = From pp In persVogel
Where pp.Company.Name = "PHVIS"
Select pp
In the second sample, persVogel is a subset of records from the People table (specifically, the subset of people with LastName == "Vogel") - so what you get is a set of People records.
The second linq statement is based on this subset of records (From pp In persVogel) and further filters them down to records where pp.Company.Name = "PHVIS". What you get is still a set of People records.
These two statements could easily be compressed into one single statement:
Dim persVogelPHVIS = From p In db.People
Where p.LastName = "Vogel"
AndAlso p.Company.Name = "PHVIS"
Select p
You will still get a set of People records at the end.

How to reference the same table twice?

First of all I have to admit I'm very much a novice in Linq and Lambda expressions.
I'm trying to get the following SQL statement into a Linq statement (using lamda expressions):
select *
from dbo.tblStockTransfers t1,
dbo.tblSuppliers t2
where t1.SupplierID = t2.SupplierID
and t2.WarehouseID in (1,2,3)
and t1.GoodsPickedUp = 1
and Not exists
(select 1 from dbo.tblStockTransfers t3
where t3.TransferOutID = t1.TransferID and t3.TransferConfirm = 1)
My class StockTransfer is an aggregate root and has it's own repository.
Now so far I got the following (the variable allowedWarehouses contains the list of warehouse IDs):
Return GetObjectSet().Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True)
This works fine, but obviously is missing the " and not exists ..." part (the last 3 lines of the SQL code at the top of this posting).
I know that Linq doesn't have a "not exists", but you can use the "Any" method for this.
Here's a working example of this elsewhere in my code:
Return GetObjectSet().Where(Function(sw) sw.Active = True And Not sw.Suppliers.Any(Function(sp) sp.WarehouseID = sw.Id))
This works fine and will give me any warehouses which are not linked to a supplier yet.
As you can see in the above example this is fine as I'm referring to the related table "Suppliers".
However, in the SQL code I'm now trying to convert into Linq, the "not exists" is not on a linked table but on itself. Is there a way I can create a 2nd reference on the main table and use that in a ".. not ..any" part. Maybe something like:
Return GetObjectSet().Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True And Not st2.Any(st2.TransferOutID = st.TransferId and st2.TransferConfirm = true)
But I don't know how to define st2 (i.e. in this case st2 would be a 2nd alias to StockTransfer).
Any Help would be greatly appreciated.
This is not the answer to the question, but it is a work-around which does get me the result I need:
Dim st1 As List(Of StockTransfer) = GetObjectSet.Where(Function(st) allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True).ToList
Dim st2 As List(Of StockTransfer) = GetObjectSet.Where(Function(st) st.TransferConfirm = True).ToList
For Each st As StockTransfer In st2
st1.RemoveAll(Function(x) x.Id = st.TransferOutID)
Next
Return st1
I'm obviously cheating by splitting out the query in 2 parts, where each part ends up in a list and then I remove from list 1 any items which I've got in list 2 (removing the ones which would normally be ignored by the "not exists" part).
However, I would love to hear it if anyone can come up with a working solution using Linq and lambda expressions (as this does feel a bit like a cheat).
I would do it something like this:
Dim lsWareHouseIds As New List(Of Integer)() From {1,2,3}
dim obj= ( _
From t1 in db.tblStockTransfers _
join t2 in db.tblSuppliers _
on t1.SupplierID equals company.SupplierID _
where lsWareHouseIds.Contains(t2.WarehouseID) _
andalso t1.GoodsPickedUp =1 _
andalso Not _
(
from t3 in db.tblStockTransfers _
where t3.TransferConfirm=1 _
select t3.TransferOutID _
).Contains(t1.TransferID) _
select t1 _
)
I did see you comment and you answer. Can't you do like this:
GetObjectSet.Where(Function(st) _
allowedWarehouses.Contains(st.Supplier.WarehouseID) And st.GoodsPickedUp = True _
Andalso Not _
GetObjectSet.Where(Function(st) _
st.TransferConfirm = True).Any(Function(x) x.Id = st.TransferOutID)).ToList

Need help with designing a query in ELinq

This is my query:
Dim vendorId = 1, categoryId = 1
Dim styles = From style In My.Context.Styles.Include("Vendor") _
Where style.Vendor.VendorId = vendorId _
AndAlso (From si In style.StyleItems _
Where si.Item.Group.Category.CategoryId = _
categoryId).Count > 0 _
Distinct
I have the feeling that I can improve the performance, cuz the above query is (correct me if I am wrong) performs 2 round-trips to the server; 1 time by the Count and then when it's executed.
I want to send this Count thing to the DB so it should be only one round trip to the server.
Even it's not the exact thing, this is actually what I need:
SELECT DISTINCT Style.*
FROM Style INNER JOIN
Vendor ON Style.VendorId = Vendor.VendorId INNER JOIN
StyleItem ON Style.StyleId = StyleItem.StyleId INNER JOIN
Item ON StyleItem.ItemId = Item.ItemId INNER JOIN
[Group] ON Item.GroupId = [Group].GroupId INNER JOIN
Category ON [Group].CategoryId = Category.CategoryId
WHERE (Style.VendorId = #vendorid) AND (Category.CategoryId = #CategoryId)
I wish I could use this SPROC (i.e. function import etc.), but I need to Include("Vendor"), which constraints me to do it with Linq.
Any kind of suggestion will be really welcommed!
It is probably not doing two trips to the database. It will get optimized before it is executed, and nothing gets executed until you try the read the data.
Normally I check the SQL that is created using SQL Profiler. I have also found LinqPad to be very usefull.

Can't use "Skip" in LINQ query

I have a linq query in which I need to specifically do a left join. However when I attempt to commit a lambda Skip function on the query it errors and says that the skip cannot be performed on a linq query with a join.
Here's the query (the skip variable is a parameter into the function and clientDB is the datacontext):
Dim questionsQuery = From helpQuestion As HelpQuestion In clientDB.HelpQuestions _
Group Join helpCat As HelpCategory In clientDB.HelpCategories _
On helpCat.ROW_ID Equals helpQuestion.CATEGORY_ID Into helpGroup = Group _
From helpCategory In helpGroup.DefaultIfEmpty() _
Where helpQuestion.DISPLAY_DESK _
Order By helpQuestion.ROW_ID Descending _
Select helpQuestion.ROW_ID, helpQuestion.EMAIL, helpQuestion.FIRST_NAME, helpQuestion.LAST_NAME, helpQuestion.QUESTION, helpQuestion.CREATED, helpQuestion.RESPONSE, helpCategory.CATEGORY_NAME
If skip > 0 Then
questionsQuery = questionsQuery.Skip(skip)
End If
I ended up just converting this to a list using questionsQuery.ToList(). Not the best solution because the ToList function returns the entire result set to an in-memory list, but it worked.

Linq Join Question

I have a problem trying to do a couple of things with linq joins... currently I have a
group in linq that gives two columns, basically a count of tickets by location. Well now I'm trying to add a join that will join on the ticketID columns of two different tables Tickets and Comments.
I'm having a hell of a time trying to convert the sql join into Linq, less alone merging that into my original total count linq statement...somebody please help!
Original Linq statement:
From p In NewTickets _
Where p.TDate > "06/01/2009 12:00:00AM" And p.TDate < "07/01/2009 12:00:00PM" _
Group p By p.LocationID _
Into Count() _
Select _
LocationID, _
NoOfTickets = Count _
Order By NoOfTickets Descending
Join I need merged into Linq statement:
SELECT *
FROM NewTickets as p
LEFT OUTER JOIN NewComments AS c ON p.TicketID = c.TicketID
WHERE (p.TDate > '06/01/2009 12:00:00AM') And (p.TDate < '07/01/2009 12:00:00PM')
AND c.Comment Like '%ali%'
THANK YOU!
If you add a relationship in the linq to sql designer between NewTickets and NewComments, properties will be created on those classes to navigate.
Queries that use those properties will automatically translate into the join. For example:
from t in db.NewTickets
where t.NewComments.Any(nc => nc.Comment.Contains("ali"))
group t by t.LocationID into g
select new {LocationID = g.Key, NoOfTickets = g.Count()} into x
order x by x.NoOfTickets descending
select x;
Apologies for the C# code examples.
Also, I'd like to point out that the left join in your sql is moot - tickets that have no comments will be removed by the ali criteria. An inner join will do fine.
Something like this
var movies = NewTickets.Join(NewComments, x => x.TicketID, y => y.TicketID, (x, y) => x).ToList();
That was it...for the most part...I'm going to have to attack this a different way as now I'm getting a count that apparently from comments as my total has ballooned from under 200 to almost 1300...each ticket will have on average around 5 or so comments so that's why I assuming this just shooting from the hip...
Thank you David and no problem with the C# (as much that I have translated, you think I'd be using it by now).
For anyone using VB that would like to see the same in VB, here you go:
Dim q = From e In db.NewTickets _
Where e.NewComments.Any(Function(nc) nc.Comment.Contains("ali")) _
Group e By e.LocationID _
Into Count() _
Select _
LocationID, _
NoOfTickets = Count _
Order By NoOfTickets Descending

Resources