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.
Related
before I return "set.select" I would like to include fields from another table but I canot join this table because it has no fields in common with the other two tables. How may I adjust my code below to achieve this? Iam using vs2012 sql and in MVC c#
var set =
(from m in managers
from t in context.tblCompany
join tsc in context.tblStyling on t.ccID equals tsc.ccID
select new { tsc.ccID,LogoIcon = tsc.Icon , tsc.style1, tsc.style2, t.Desc })
.ToList();
return set.Select(c => new Settings(c.ccID, c.style1, c.style2, c.Desc, c.LogoIcon , m.firstName , m.lastName));
Okay, it looks like you want to join context.tblCompany and context.tblStyle and get the cross product of the resulting set and managers. If that is correct, then you are already there. You just need to include the fields from manager that you want in your select statement:
var set =
(from m in managers
from t in context.tblCompany
join tsc in context.tblStyling on t.ccID equals tsc.ccID
select new
{
tsc.ccID,
LogoIcon = tsc.Icon,
tsc.style1,
tsc.style2,
t.Desc,
m.firstName,
m.lastName })
.ToList();
return set;
I am trying to convert the following sql query to LINQ statement
SELECT t.*
FROM (
SELECT Unique_Id, MAX(Version) mversion
FROM test
GROUP BY Unique_Id
) m INNER JOIN
test t ON m.Unique_Id = t.Unique_Id AND m.mversion = t.Version
LINQ statement
var testalt = (from altt in CS.test
group altt by altt.Unique_Id into g
join bp in CS.alerts on g.FirstOrDefault().Unique_Id equals bp.Unique_Id
select new ABCBE
{
ABCName= bp.Name,
number = bp.Number,
Unique_Id = g.Key,
Version = g.Max(x=>x.Version)
});
I am getting an error of where clause. Please help
SQL FIDDLE
This is not an easy straight forward conversion but you can accomplish the same thing using linq method syntax. The first query is executed to an expression tree, then you are joining that expression tree from the grouping against CS.alerts. This combines the expression tree from CS.test query into the expression tree of CS.alerts to join the two expression trees.
The expression tree is evaluated to build the query and execute said query upon enumeration. Enumeration in this case is the ToList() call but anything that gets a result from the enumeration will execute the query.
var query1 = CS.test.GroupBy(x => x.Unique_Id);
var joinResult = CS.alerts.Join(query1,
alert => new { ID = alert.Unique_Id, Version = alert.Version },
test => new { ID = test.Key, Version = test.Max(y => y.Version },
(alert, test) => new ABCBE {
ABCName = alert.Name,
number = alert.Number,
Unique_Id = test.Key,
Version = test.Max(y => y.Version)
}).ToList();
Because query1 is still an IQueryable and you are using CS.alerts (which I'm guessing CS is your data context) it should join and build the query to execute upon the ToList() enumeration.
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
};
LINQ gurus, I am looking for help to write a query...
I have a table with Person records, and it has a nullable ParentID column, so it is kind of self-referencing, where each record might have a Parent.
I am looking for unprocessed rows whose parent rows were processed.
This SQL works fine:
SELECT *
FROM Person
where IsProcessed = 0 and
ParentId in
(
select Id from Person
where IsProcessed = 1
)
I tried a number of LINQ queries, but they failed. Now, I'm trying:
var qParent =
from parent in db.Person
where
parent.IsProcessed == true
select parent.ID;
var qChildren = from child in db.Person
where
child.IsProcessed == false
&& child.ParentId.HasValue
select child.ParentId.Value;
var q2 = qChildren.Intersect(qParent);
This yields SQL with a DISTINCT clause, for some reason, and I am baffled why DISTINCT is generated.
My main question is how to write LINQ for the SQL statement above?
Thanks in advance.
Intersect is a set operation - it is meant to return a set of distinct elements from the intersection. It seems reasonable to me that it would use DISTINCT in the SQL. There could be multiple children with the same parent, for example - Intersect should only return that ID once.
Is there any reason you don't want to use a join here?
var query = from parent in db.Person
where parent.IsProcessed
join child in db.Person.Where(child => !child.IsProcessed)
on parent.ID equals child.ParentId.Value
select child;
The query can be translated literally into :
var parentIds = db.Person.Where(x => x.IsProcessed)
.Select(x => x.Id)
.ToList();
var result = db.Person.Where(x => !x.IsProcessed && parentIds.Contains(x => x.Id))
.ToList();
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);