Lambda Expression Select Min and Max at the same time - c#

How do I convert this code to a Lambda expression when there are two columns to select at the same time?
LINQ
var lq=(from a in tbl
group a by 0 into b
select new { intYear = b.Min(p => p.intYear), tintMonth = b.Max(p => p.tintMonth) }
).SingleOrDefault();
T-SQL
SELECT MIN(intYear), MAX(tintMonth)
FROM tbl
Lambda Expression
tbl.Select(x => x.intYear).Min(); //Can't figure how to select if 2 columns

If you're intent on returning a "row" rather than two values, you can group all the rows together as you do in the first LINQ expression:
tbl.GroupBy(t => 1)
.Select(g => new { intYear = g.Min(p => p.intYear), tintMonth = g.Max(p => p.tintMonth) })
Note, I assume this is for LINQ-to-SQL. For plain old objects, this would most likely result in three iterations through the collection. One to do the grouping, one for the Min(), and one for Max(). For large collections, you would be be better off looping once and doing it the good-ol'-fashioned way with a single foreach over the collection.

Related

LINQ: unable to use group by and sort

I am new to LINQ and I need to write a query that should get the grouped records order by date. My table has columns: personId, monthAccepted, amountSent, processKeyId, dateProcessed
A personId can have multiple entries. My requirement is to get the first entry(dateProcessed) for every distinct personId order by processKeyId. This is what I have tried:
int pageNumber = 1;
int pageSize = 100;
var RecordsInQueue = from o in db.PersonTransaction
.OrderByDescending(o => o.processKeyId)
.GroupBy(g => g.personId)
select o;
return RecordsInQueue.ToPagedList(pageNumber, pageSize));
When running the above query I am getting the following error:
The method 'Skip' is only supported for sorted input in LINQ to
Entities. The method 'OrderBy' must be called before the method
'Skip'.
How can I select the correct records using LINQ?
My guess is that you need to do OrderBy on your RecordsInQueue.
Although you sorted the PersonTransaction list to produce the RecordsInQueue list, you didn't actually sort that list.
You can probably just do OrderByDescending(c => c.processKeyId).
The code would be:
int pageNumber = 1;
int pageSize = 100;
var RecordsInQueue = from o in db.PersonTransaction
.OrderByDescending(o => o.processKeyId)
.GroupBy(g => g.personId)
select o;
return RecordsInQueue.OrderByDescending(c => c.processKeyId).ToPagedList(pageNumber, pageSize));
This is just a guess as I don't know what your ToPagedList() method is doing. But it seems most likely that it is doing .Skip() to jump to the appropriate page, which it can't reliably do without knowing what order to use to skip.
you could try
var recordInQueue = db.PersonTransaction.OrderByDescending(x => x.processKeyId)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.GroupBy(g.PersonId)
.Select(r => new {r.Key, r.Value}).ToList();
Your query, which is not ordered, and returns unordered grouped objects. You need to order them before you call RecordsInQueue.ToPagedList.
Something like this:
var query =
from pt in db.PersonTransaction
group prod by pt.personId into grouping
select new
{
Id = grouping.Key,
TotalOfSomething = grouping.Sum(p => p.Something)
};
// unordered query
var ordered = query.OrderBy( pt => pt.TotalOfSomething );
//now you can skip and take
Please note that in your query the result will not contain processKeyId since it's not in the groupby expression.

Selecting top two of the combined group by linq

I am trying to figure out how to pick the last two "Transactions" from my query.
My Query looks like this
var summary= (from tType in _context.CommandCentre.TransactionTypes
join tSummary in _context.CommandCentre.TransSummary on tType.Id equals tSummary.TransactionType
where tSummary.ChargeFlag.ToLower() == ChargeFlag.Overcharge.ToString().ToLower()
group tSummary by new { tType.Name, tSummary.NumberOfTransactions, tSummary.PeriodDate }
into gResult
select new
{
Fee = gResult.Key.Name,
TransactionCount = gResult.Key.NumberOfTransactions,
Period = gResult.Key.PeriodDate,
ActualAmount = gResult.Sum(x => x.ActualAmount),
}).OrderByDescending(x=>x.Period);
Now if I do a Take(2) I get only the last two records, while I want to get the last two records for every "Fee" of my selection. Basically Two records for every "Fee" ordered by "Period" date.
not sure how to do this in a single query.
Try this :
var result = summary.GroupBy(p => p.Fee)
.SelectMany(d => d.OrderBy(r => r.Period).Take(2))
.ToList();

What is the LINQ/Lambda equivalent to Select Count(*), SUM(someColumn) From SomeTable

I don't think I should have to group by a particular column to return aggregations for SUM and COUNT in LINQ or lambda expressions.
Is there a quick and efficient way to reproduce the following T-SQL query in LINQ or as a Lambda Expression
SELECT COUNT(*), SUM(SomeIntegerColumn) From SomeTable
Maybe there is something I am missing
Try this:
from x in SomeTable
group x by 1 into g
select new
{
Count = g.Count(),
Sum = g.Sum(x => x.SomeIntegerColumn)
}
The trick is the "dummy" grouping that lets you compute several things in one query.
If you really want to do it without a group by you can:
var sumCount =
new { count = yourCollection.Count(), sum = yourCollection.Sum(i => i.SomeIntegerColumn) };

select matching object twice in a linq where-in clause

I know that we can use the Contains method in generating a where clause in a linq query like this:
List<long> objectIDs = new List<long>() { 1, 1, 2 };
var objects = dbcontext.Where(o => objectIDs.Contains(o.ID))
.Select(o => o).ToList();
My question is, how will I be able to select the matching object twice if the ID occurs twice in the where condition?
It sounds like you want to select a separate copy of the object for each match.
Select() can only return exactly one object; you need SelectMany():
list.SelectMany(p => Enumerable.Repeat(p, objectIDs.Count(id => id == p.ID)))
You could also do this faster using a join.

LINQ: Doing an order by!

i have some Linq to Entity code like so:
var tablearows = Context.TableB.Include("TableA").Where(c => c.TableBID == 1).Select(c => c.TableA).ToList();
So i'm returning the results of TableA with TableB.TableBID = 1
That's all good
Now how can I sort TableA by one of its column? There is a many to many relation ship between the two tables
I tried various ways with no look, for example
var tablearows = Context.TableB.Include("TableA").Where(c => c.TableBID == 1).Select(c => c.TableA).OrderBy(p => p.ColumnToSort).ToList();
In the above case when i type "p." i don't have access to the columns from TableA, presumably because it's a collection of TableA objects, not a single row
How about using SelectMany instead of Select :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.SelectMany(c => c.TableA)
.OrderBy(p => p.ColumnToSort)
.ToList();
EDIT :
The expression below returns collection of TableAs -every element of the collection is an instance of TableA collection not TableA instance- (that's why you can't get the properties of the TableA) :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.Select(c => c.TableA);
If we turn the Select to SelectMany, we get the result as one concatenated collection that includes elements :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.SelectMany(c => c.TableA);
Okay, so now I've taken on board that there's a many to many relationship, I think Canavar is right - you want a SelectMany.
Again, that's easier to see in a query expression:
var tableARows = from rowB in Context.TableB.Include("TableA")
where rowB.TableBID == 1
from rowA in rowB.TableA
orderby rowA.ColumnToSort
select rowA;
The reason it didn't work is that you've got a different result type. Previously, you were getting a type like:
List<EntitySet<TableA>>
(I don't know the exact type as I'm not a LINQ to Entities guy, but it would be something like that.)
Now we've flattened all those TableA rows into a single list:
List<TableA>
Now you can't order a sequence of sets by a single column within a row - but you can order a sequence of rows by a column. So basically your intuition in the question was right when you said "presumably because it's a collection of TableA objects, not a single row" - but it wasn't quite clear what you mean by "it".
Now, is that flattening actually appropriate for you? It means you no longer know which B contributed any particular A. Is there only actually one B involved here, so it doesn't matter? If so, there's another option which may even perform better (I really don't know, but you might like to look at the SQL generated in each case and profile it):
var tableARows = Context.TableB.Include("TableA")
.Where(b => b.TableBID == 1)
.Single()
.TableA.OrderBy(a => a.ColumnToSort)
.ToList();
Note that this will fail (or at least would in LINQ to Objects; I don't know exactly what will happen in entities) if there isn't a row in table B with an ID of 1. Basically it selects the single row, then selects all As associated with that row, and orders them.

Categories

Resources