I am trying to craft a LINQ statement as follows:
The result should follow between two dates (today basically) OR
the result should have a "batchstatus" column that equals false (hasn't been verified yet)
the result should have a "ready" column that equals true (is ready to be verified).
So verifiers can see all data from today regardless if its been verified or not, BUT shouldn't see any that the users are not ready to be seen yet.
I have tried this several different ways such as:
Dim p = From t In db.batches _
Where t.bDate > day1 And t.bDate < day2 And t.Ready = True Or t.BatchStatus = False _
Order By t.BatchStatus Ascending _
Select t
Please help me keep my hair; I have a handful now & I don't know how much longer I can keep from yanking it out!!!
Thanks!
Use brackets. It'll make things a lot simpler. Additionally, you can use Let clauses to make your query simpler, effectively giving you "local variables" in the query. Try this - the syntax may be slightly off (I'm not a VB guy) and the logic may very well not be what you want, but it's easier to understand IMO, so should be easier to modify.
Dim p = From t In db.batches _
Let createdToday = t.bDate > day1 And t.bDate < day2 _
Where createdToday And (t.Ready Or Not t.BatchStatus) _
Order By t.BatchStatus Ascending
What about:
Dim p = From t In db.batches _
Where (t.bDate.Date = DateTime.Today Or t.BatchStatus = False) _
And t.Ready = True _
Order By t.BatchStatus Ascending _
Select t
Thanks Jon!!! It was parenthesis in my case:
Dim p = From t In db.batches _
Where (t.bDate > day1 And t.bDate < day2 And t.Ready = True) Or (t.BatchStatus = False And t.Ready = True) _
Order By t.BatchStatus Ascending _
Select t
Thanks again....hair slowly being removed from death grip...
Related
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
I have a long LinqtoSQl query in which several parameters I'm not forcing the user to specify anything. I started using a Select Case statement that would test rather or not a parameter string's length > 0 or if it's an int > 0. But I realized that I would have to test for each possibility and create queries based on each.
I did some searching and ran across a post in which the person answering the post was saying to negate a portion of the query use ||. After doing some more searching (and realizing with little c# skills I do have || is the OR conditional), I realized that wouldn't help me.
I guess what I want to do is something like
Dim r = From x in db.List _
(if firstName.Length < 1 then ignore query
else)where x.firstName = firstName _
(if lastName.Length < 1 then ignore query
else)where x.LastName = lastName _
Select x
I knw there has to be a better way than IfElse'ing my way through this...I was about to do some funky stuff with a StringBuilder, but I'm not sure it would "fire", ie:
Dim sb as New StringBuilder
sb.Append("Dim r = From x in db.List _")
If firstName.Length < 1 then
sb.Append("Where x.firstName = firstName")
ughh, please tell me there's a better way...
Thanks for your help!
Use the fact that queries are composable. I'll write this in C# to start with, then translate it into VB afterwards if you need that. The principle would be the same.
IQueryable<YourEntityType> query = db.List;
if (firstName != "")
{
query = query.Where(x => x.firstName == firstName)
}
if (lastName != "")
{
query = query.Where(x => x.lastName == lastName)
}
Now just read from query appropriately. (I've changed the nature of the string conditions just because it simpler to understand "is this string the empty string" than "is this string's length greater than 0" - both will work, obviously.)
Note that you can't do this sort of conditional call in a query expression, but it's easy when you're just calling the extension methods explicitly.
How about...
Dim r = From x in db.List _
where (x.firstName = firstName Or firstName = "") _
And (x.LastName = lastName Or lastName = "") _
Select x
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.
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
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.