I'm trying to return a result set from a grouped query and I can't get the select right. In LinqPad the cursor jumps to "ItemID" in Grouped.Key.ItemID with the error:
'int' does not contain a definition for 'ItemID' and no extension method 'ItemID' accepting a first argument of type 'int' could be found
Here's the query:
from B in Bids
join I in Items on B.ItemID equals I.ItemID
group new {B, I} by I.ItemID into Grouped
select new {
ItemID = Grouped.Key.ItemID,
ItemName = Grouped.Key.ItemName,
Bids = Grouped.Key.B
}
I would like the return set to have records comprised of the ItemID, ItemName and all of the associated Bid records.
Thanks very much,
BK
The Grouped.Key refers to the field(s) that you specied in the grouped by x clause. As a result in your query, the Key = I.ItemID.
In your example, instead of thinking from the SQL perspective where you have to flatten out heirarchies, embrace the OO nature of LINQ and object graphs. Adapting your example a bit and setting LINQPad to use C# Statements, I think you will end up with more of what you are looking for. Note: The Dump() extension method is specific to LINQPad to output the results and shows the resulting heirarchy.
var bids = new [] { new { ItemID = 1, BidValue = 30 } , new {ItemID=1, BidValue=45}};
var items = new [] { new { ItemID = 1, ItemName = "x" }, new {ItemID = 2, ItemName="y"} };
var query = from i in items
select new
{
i.ItemID,
i.ItemName,
Bids = from b in bids
where b.ItemID == i.ItemID
select b
};
query.Dump();
That being said, your categories indicate LINQ to SQL. If your model is in LINQ to SQL or EF, you may be able to do this even easier by using the mapped associations:
var query = from i in dc.Items
select new
{
i.ItemID,
i.ItemName,
i.Bids
};
query.Dump();
That says exactly what is written. Groupped.Key will contain I.ItemID, but not the whole I. So you can't write Groupped.Key.ItemID.
Consider:
from B in new [] { new { ItemID = 1, BidValue = 30 } }
join I in new [] { new { ItemID = 1, ItemName = "x" } } on B.ItemID equals I.ItemID
group new { B, I } by I into Groupped
select new {
ItemID = Groupped.Key.ItemID,
ItemName = Groupped.Key.ItemName,
Bids = (from g in Groupped select g.B).ToList()
}
Well, assuming you have foreign keys setup in the database from bid -> item there is no need for all this joining an grouping.
Your items will already have a collection of bids in them.
So you can do things like:
var x = db.Items.Single(i=>ItemId == 1); // get one item
foreach (bid b in x.Bids) // iterate through all the bids
{}
If you really want to have them in an anonymous type, this will do:
from i in db.items
select new { i.ItemID, i.ItemName, i.Bids }
That is the beauty of Linq2Sql. Try to let go of writing SQL in Linq but instead use the more object oriented approach.
In this groupby, ItemID is the Key. ItemID does not have a B property.
group new {B, I} by I.ItemID into Grouped
Here's an improved version of your query which accesses the group properly.
from b in Bids
join i in Items on b.ItemID equals i.ItemID
group b by i into groupedBids
select new
{
Item = i,
Bids = groupedBids
};
Here's a version that uses GroupJoin to do the same thing.
from i in Items
join b in Bids on i.ItemID equals b.ItemID into groupedBids
select new
{
Item = i,
Bids = groupedBids
};
Here's a version that does the join in the database and the group locally. You might do something like this since LinqToSql must re-query by the key of a group to get each group's elements (known as the n+1 problem with groupby).
from x in
(
from i in Items
join b in Bids on i.ItemID equals b.ItemID
select new {Item = i, Bid = b}
).ToList()
group x.b by x.i into groupedBids
select new
{
Item = groupedBids.Key,
Bids = groupedBids
};
Related
I am trying to convert the following T-SQL statement into Linq to Sql but am having trouble with the subtraction from the count. The final select will be a single row and single column (int)
I have done the SQL in two ways (sub-query and by JOIN/GROUP) which both return the same result, although I think the former might be the 'easier' option...
SQL 1 using a sub-query...
SELECT e.Places - ( SELECT COUNT(*) FROM [Event Participants] ep WHERE ep.E__ID = x AND ep.EP_STAT IN ('B','C')) AS AvailablePlaces
From Events e
WHERE e.E__ID = x
SQL 2 using GROUP BY and JOIN...
SELECT e.Places - COUNT(ep.E__ID) AS AvailablePlaces
FROM Events e
JOIN [Event Participants] ep ON e.E__ID = ep.E__ID
WHERE e.E__ID = x AND ep.EP_STAT IN ('B','C')
GROUP BY e.Places
Something like
var array = new string[] { "B", "C" };
var result = (from e in Event where e.E__ID == x
let count = (from ep in Event_Participants
where ep.E__ID == e.E__ID &&
array.Contains(ep.EP_Stat)
select ep).Count()
select e.Places - count
)
.Single();
Depending on your model, it might be possible to use navigation properties in the subquery.
I'm running ASP.NET MVC + LINQ-to-SQL using .NET 4.0.
I have query similar to this:
var labels = (from c in db.Customers
join o in db.Orders
on c.CustID equals o.CustID
select o).Distinct();
Can I do the same with an anonymous query, something like this
Customers.Select(x => x.Orders) ??
How would I do this if my query depth is 3 or 4 tables deep?
The query you have there is more or less equivalent to this:
var labels = db.Customers
.Join(db.Orders, c => c.CustID, o => o.CustId, (c, o) => o)
.Distinct();
FYI, it can go by different names. "Lambda" syntax is a common one. "Fluent" syntax is another. There's nothing anonymous about it. Anonymous types on the other hand is an unnamed type defined like so:
var anonymousObject = new { SomeField = 2, SomeOtherField = "Foo" };
I have a history table for Students in SQL Server 2008.
StudentHistoryId, StudentId, Grade, CreatedAt, ModifiedAt, ModifiedBy, Active
I am new to LINQ.
How do I write a LINQ query to get the latest modified row for all the active students and also the equivalent sql query for the same ?
Something like (Assuming LINQ-SQL):
using (YourDataContext db = new YourDataContext())
{
var data = from s in db.Students
select new
{
StudentId = s.StudentId,
LastHistory = s.Histories
.OrderByDescending(s => s.ModifiedAt)
.Where(s => s.Active)
.FirstOrDefault()
};
}
This is assuming that you want all students, regardless of whether they actually have any history. If don't want this, you can start with the History table and group by Student ID.
To view the SQL, you can hover the variable in debugging to see the SQL produced. I'm too lazy to convert the LINQ ;-)
var q =
from h in history
where h.Active
group h by new { h.StudentId, h.Grade } into g
select new
{
StudentId = g.Key.StudentId,
Grade = g.Key.Grade,
LatestModified = g.Max (x => x.ModifiedAt)
}
LINQ
var tempresult = (from student in Students
where Active == true).OrderByDesc(ModifiedAt)
List<Student> results = new List<Student>();
foreach(var result in tempResult)
{
if((results.Where(r => r.StudentId == result.StudentId).FirstOrDefault()) == null)
{
results.Add(result);
}
}
SQL
Select [Rows]
From Students S
Where S.Active = 1
And S.ModifiedAt = (Select Max(ModifiedAt)
From Student S1
Where S1.StudentId = S.StudentId)
The Linq is hacky (and I'm sure there's a better way, but I can't remember it) and I'm only sort-of confident about the SQL syntax (though that should point you in the right direction even if it's not exactly right), but either of those should get: The maximum ModifiedAt for every student that is currently active.
.FirstOrDefault() [LINQ] or Top 1 would only select the single row (only one student) with the most recent ModifiedAt.
I've written a simple linq query as follows:
var query = from c in context.ViewDeliveryClientActualStatus
join b in context.Booking on c.Booking equals b.Id
join bg in context.BookingGoods on c.Booking equals bg.BookingId
select new { c, b, bg };
I have filtered the previous query with a number of premises and then needed to group by a set of fields and get the sum of some of them, as so:
var rows = from a in query
group a by new {h = a.c.BookingRefex, b = a.c.ClientRefex, c = a.b.PickupCity, d = a.b.PickupPostalCode} into g
select new
{
Booking_refex = g.Key.h,
Client_refex = g.Key.b,
//Local = g.
Sum_Quan = g.Sum(p => p.bg.Quantity),
};
I'd like to get a few values from a which I haven't included in the group by clause. How can I get those values? They're not accessible through g.
The g in your LINQ expression is an IEnumerable containing a's with an extra property Key. If you want to access fields of a that are not part of Key you will have to perform some sort of aggregation or selection. If you know that a particular field is the same for all elements in the group you can pick the value of the field from the first element in the group. In this example I assume that c has a field named Value:
var rows = from a in query
group a by new {
h = a.c.BookingRefex,
b = a.c.ClientRefex,
c = a.b.PickupCity,
d = a.b.PickupPostalCode
} into g
select new {
BookingRefex = g.Key.h,
ClientRefex = g.Key.b,
SumQuantity = g.Sum(p => p.bg.Quantity),
Value = g.First().c.Value
};
However, if c.Value is the same within a group you might as well include it in the grouping and access it using g.Key.cValue.
Just add those field in the
new {h = a.c.BookingRefex, b = a.c.ClientRefex, c = a.b.PickupCity, d = a.b.PickupPostalCode}
they will be accessible in g then.
I have been playing with the Linq to Sql and I was wondering if it was possible to get a single result out? For example, I have the following:
using(DataClassContext context = new DataClassContext())
{
var customer = from c in context.table
where c.ID = textboxvalue
select c;
}
And with this I need to do a foreach around the var customer but i know that this will be a single value! Anyone know how I could do a textbox.text = c.name; or something along that line?
Yes, it's possible.
using(DataClassContext context = new DataClassContext())
{
var customer = (from c in context.table
where c.ID = textboxvalue
select c).SingleOrDefault();
}
This way you get 1 result or null if there isn't any result.
You can also use Single(), which throws an exception when there isn't a result.
First() will give you only the first result found, where Last() will give you only the last result, if any.
Here's an Overview of all Enumerable methods.
var customer = context.table.SingleOrDefault(c => c.ID == textboxvalue);