How get max value by lambda query in C#? - c#

I want to get the last number in the code column in C# using the query lambda. Be careful that I want a numerical value, not a list. For example, if the last registered number was 50, I would like this number 50. That is, we can store the query result in a numerical variable so that I can use it elsewhere
var maxcode= dbContext.personel
.Select(a => a.Max(w => w.code))
.FirstOrDefault();
For example
code name old
-----------------
1 Amelia 18
2 Olivia 27
3 Emily 11
4 Amelia 99
I want to get number 4
If I want to use top(1) to improve the speed?

This should work:
var max = dbContext.personel.Max(x => x.code);

While SQL and LINQ share some similarities, they are quite different in how they work. It's best to start with how IEnumerable works with this type of query, then how that translates to IQueryable. In most simple cases the two look exactly the same, by design.
The Select extension for IEnumerable iterates through the sequence and passes each object to the supplied function, collecting the results in a new IEnumerable of the appropriate type. In your code a will be a record rather than a collection.
Essentially Select looks like this under the hood:
public static IEnumerable<TResult> Select<TElement, TResult>(this IEnumerable<TElement> seq, Func<TElement, TResult> selector)
{
foreach (var item in seq)
yield return selector(item);
}
In simple words Select is a transformation. It takes objects of one type and passes them through a transform function.
The Max extension - at least the relevant one - processes a sequence of objects, uses the supplied function to pull some value from each object, then returns the largest of those values. It looks a little bit like this pseudo-code:
public static TResult Max<TElement, TResult>(this IEnumerable<TElement> seq, Func<TElement, TResult> valueFunc)
{
var result = default(TResult);
foreach (var item in seq)
{
var curr = valueFunc(item);
if (result == default(TResult) || result < curr)
result = curr;
}
return curr;
}
OK that won't compile, but it shows the basic concept.
So if you had an array of Personel objects in memory and wanted to find the largest code then you'd do this:
var maxCode = personel.Max(p => p.code);
The nice thing about LinqToSQL and pretty much all LINQ-like ORMs (Entity Framework, LinqToDB, etc) is that the exact same thing works for IQueryable:
var maxCode = dbContext.personel.Max(p => p.code);
The actual SQL for that will look something like (actual output from LinqToDB code gen):
SELECT
Max([t1].[code]) as [c1]
FROM
[personel] [t1]
For more interesting queries the syntax differs.
You have two Amelia entries with different ages. Let's say you want to find the age range for each name in your list. This is where grouping comes in.
In LINQ query syntax the query would look something like this:
var nameAges =
from p in dbContext.personel
group p.old by p.name into grp
select new { name = grp.Key, lowAge = grp.Min(), highAge = grp.Max() };
Grouping is easier in that format. In fluent it looks more like:
var nameAges = dbContext.personel
.GroupBy(p => p.name, p => p.old)
.Select(grp => new { name = grp.Key, lowAge = grp.Min(), highAge = grp.Max() };
Or in SQL:
SELECT name, Min(code) AS lowAge, Max(code) AS highAge
FROM personel
GROUP BY name
The moral is, writing LINQ queries is not the same as writing SQL queries... but the concepts are similar. Play around with them, work out how they work. LINQ is a great tool once you understand it.

Related

Linq challenge: converting this piece of code from method chain to standard Linq

The challenge is about converting from method chain to standard linq a piece of code full of group by.
The context
To fully understand the topic here you can read the original question (with class definitions, sample data and so on): Linq: rebuild hierarchical data from the flattened list
Thanks to #Akash Kava, I've found the solution to my problem.
Chain method formulation
var macroTabs = flattenedList
.GroupBy(x => x.IDMacroTab)
.Select((x) => new MacroTab
{
IDMacroTab = x.Key,
Tabs = x.GroupBy(t => t.IDTab)
.Select(tx => new Tab {
IDTab = tx.Key,
Slots = tx.Select(s => new Slot {
IDSlot = s.IDSlot
}).ToList()
}).ToList()
}).ToList();
But, for sake of knowledge, I've tried to convert the method chain to the standard Linq formulation but something is wrong.
What happens is similar to this..
My attempt to convert it to Linq standard syntax
var antiflatten = flattenedList
.GroupBy(x => x.IDMacroTab)
.Select(grouping => new MacroTab
{
IDMacroTab = grouping.Key,
Tabs = (from t in grouping
group grouping by t.IDTab
into group_tx
select new Tab
{
IDTab = group_tx.Key,
Slots = (from s in group_tx
from s1 in s
select new Slot
{
IDSlot = s1.IDSlot
}).ToList()
}).ToList()
});
The result in LinqPad
The classes and the sample data on NetFiddle:
https://dotnetfiddle.net/8mF1qI
This challenge helped me to understand what exactly returns a Linq Group By (and how prolix is the Linq syntax with Group By).
As LinqPad clearly shows a Group By returns a List of Groups. Group is a very simple class which has just one property: a Key
As this answer states, from definition of IGrouping (IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable) the only way to access to the content of the subgroups is to iterate through elements (a foreach, another group by, a select, ecc).
Here is shown the Linq syntax formulation of the method chain.
And here is the source code on Fiddle
But let's go on trying to see another solution:
What we usually do in SQL when we do a Group By is to list all the columns but the one which have been grouped. With Linq is different.. it still returns ALL the columns.
In this example we started with a dataset with 3 'columns' {IDMacroTab, IDTab, IDSlot}. We grouped for the first column, but Linq would return the whole dataset, unless we explicitly tell him..

Why do I get a different output from analogous Linq queries?

I am trying the following code in LinqPad 5 (specifically 5.26.01)
IEnumerable<string> breeds = new List<string>{
"Fantail",
"Lahore",
"Bokhara Trumpeter",
"Rhine Ringbeater",
"Birmingham Roller",
"Pomeranian Pouter",
"Racing Homer",
"Archangel"};
IEnumerable<string> GetAllBreedsContainingLetter_Fluent(IEnumerable<string> breedlist, string letter)
{
return breedlist
.Where(breedname => breedname.Contains(letter.ToUpperInvariant()) || breedname.Contains(letter.ToLowerInvariant()))
.OrderByDescending(breedname => breedname)
.Select(breedname => breedname);
}
IEnumerable<string> GetAllBreedsContainingLetter_Query(IEnumerable<string> breedlist, string letter)
{
return breedlist = from b in breedlist
where (b.Contains(letter.ToUpperInvariant()) || b.Contains(letter.ToLowerInvariant()))
orderby b descending
select b;
}
var breedsFluent = GetAllBreedsContainingLetter_Fluent(breeds, "R");
breedsFluent.Dump();
var breedsQuery = GetAllBreedsContainingLetter_Query(breeds, "R");
breedsQuery.Dump();
I think the two functions should be analogous but I noticed something odd about the output in Linqpad. The first .Dump() is identified as an IEnumerable<String>; the second .Dump() identifies as a IOrderedEnumerable<String>.
Is this something about the queries I'm running or is it an artifact of Linqpad? I haven't found anything from Googling.
In query syntax the transformation is such that when you have a trivial projection (projecting an item to itself) it only generates a Select call when that trivial projection is the only operation in the query. Since your query contains other operations, the Select is elided entirely, for performance reasons.
A proper translation of that query syntax query into method syntax would skip the Select. The way to replicate the behavior using query syntax would require something like a second query, to do the trivial projection.

Lambda statement Intersect

Trying to work out this lambda query without doing a foreach etc. I currently have a database table with a column which contains a comma separated list of strings. It's basically a filter (it could for example be 'pants,tops,t-shirts,gloves'). In the function that queries the database is basically has a parameter that accepts a similar string.
I don't know if I'm just too tired at the moment and can't work it out but struggling. I know it will be Intersect but can't figure out the syntax.
Currently I have...
public static List<ItemListItem> GetItems(string filter = "")
{
var db = new dbConnection();
var results = (from i in db.Items
select i);
if (!string.IsNullOrEmpty (filter))
results = results.Where(x => x.Filters.Split(',').Intersect(filter.Split(',')) )
}
You need Enumerable.Any at the end of your Intersect like:
x.Filters.Split(',').Intersect(filter.Split(',')).Any()
So your query would be:
results = results.Where(x => x.Filters.Split(',')
.Intersect(filter.Split(','))
.Any());
Enumerable.Where would require an expression returning bool. Intersect would return an IEnumerable<T> and Enumerable.Where would not accept it. Adding Enumerable.Any would means return those rows where intersection resulted in Any row.

Remove OrderBy from an IQueryable<T>

I have a paging API that returns rows a user requests, but only so many at one time, not the entire collection. The API works as designed, but I do have to calculate the total number of records that are available (for proper page calculations). Within the API, I use Linq2Sql and I work a lot with the IQueryable before i finally make my requests. When I go to get the count, I call something like: totalRecordCount = queryable.Count();
The resulting SQL is interesting none the less, but it also adds an unnecessary Order By which makes the query very expensive.
exec sp_executesql N'SELECT COUNT(*) AS [value]
FROM (
SELECT TOP (1) NULL AS [EMPTY]
FROM [dbo].[JournalEventsView] AS [t0]
WHERE [t0].[DataOwnerID] = #p0
ORDER BY [t0].[DataTimeStamp] DESC
) AS [t1]',N'#p0 int',#p0=1
Because I am using the IQueryable, I can manipulate the IQueryable prior to it making it to the SQL server.
My question is, if I already have an IQueryable with a OrderBy in it, is it possible to remove that OrderBy before I call the Count()?
like: totalRecordCount = queryable.NoOrder.Count();
If not, no biggie. I see many questions how to OrderBy, but not any involving removing an OrderBy from the Linq expression.
Thanks!
So, the below code is a spike against an in-memory array. There may be some hurdles to get this working with Entity Framework (or some other arbitrary IQueryProvider implementation). Basically, what we are going to do is visit the expression tree and look for any Ordering method call and simply remove it from the tree. Hope this points you in the right direction.
class Program
{
static void Main(string[] args)
{
var seq = new[] { 1, 3, 5, 7, 9, 2, 4, 6, 8 };
var query = seq.OrderBy(x => x);
Console.WriteLine("Print out in reverse order.");
foreach (var item in query)
{
Console.WriteLine(item);
}
Console.WriteLine("Prints out in original order");
var queryExpression = seq.AsQueryable().OrderBy(x => x).ThenByDescending(x => x).Expression;
var queryDelegate = Expression.Lambda<Func<IEnumerable<int>>>(new OrderByRemover().Visit(queryExpression)).Compile();
foreach (var item in queryDelegate())
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
public class OrderByRemover : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType != typeof(Enumerable) && node.Method.DeclaringType != typeof(Queryable))
return base.VisitMethodCall(node);
if (node.Method.Name != "OrderBy" && node.Method.Name != "OrderByDescending" && node.Method.Name != "ThenBy" && node.Method.Name != "ThenByDescending")
return base.VisitMethodCall(node);
//eliminate the method call from the expression tree by returning the object of the call.
return base.Visit(node.Arguments[0]);
}
}
There isn't just an unneeded ORDER BY, there's also a spurious TOP(1).
SELECT TOP (1) NULL AS [EMPTY] ...
That subselect will only return 0 or 1 rows. In fact without the TOP there it wouldn't be legal to have an ORDER BY in a subselect.
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.: SELECT COUNT(*) FROM ( SELECT * FROM Table1 ORDER BY foo )
sqlfiddle
I think you have probably done something wrong in your LINQ. Are you sure you haven't written .Take(1) or similar somewhere in your query, before calling .Count()?
This is wrong:
IQueryable<Foo> foo = (...).OrderBy(x => x.Foo).Take(1);
int count = foo.Count();
You should do this instead:
IQueryable<Foo> foo = (...);
Iqueryable<Foo> topOne = foo.OrderBy(x => x.Foo).Take(1);
int count = foo.Count();
I am afraid there is no easy way to remove the OrderBy operator from queryable.
What you can do, however, is to re-create the IQueryable based on the new expression obtained from rewriting queryable.Expression(see here) omitting the OrderBy call.
If you can't eliminate the root cause, here is a workaround:
totalRecordCount = queryable.OrderBy(x => 0).Count();
SQL Server's query optimizer will remove this useless ordering. It won't have runtime cost.
I think you have implemented you paging code wrongly. You actually need to query the database twice, once for the paged datasource and once for the total row count. This is how the setup should look.
public IList<MyObj> GetPagedData(string filter, string sort, int skip, int take)
{
using(var db = new DataContext())
{
var q = GetDataInternal(db);
if(!String.IsNullOrEmpty(filter))
q = q.Where(filter); //Using Dynamic linq
if(!String.IsNullOrEmpty(sort))
q = q.OrderBy(sort); //And here
return q.Skip(skip).Take(take).ToList();
}
}
public int GetTotalCount(string filter)
{
using(var db = new DataContext())
{
var q = GetDataInternal(db);
if(!String.IsNullOrEmpty(filter))
q = q.Where(filter); //Using Dynamic linq
return q.Count(); //Without ordering and paging.
}
}
private static IQuerable<MyObj> GetDataInternal(DataContext db)
{
return
from x in db.JournalEventsView
where ...
select new ...;
}
The filtering and sorting is done using the Dynamic linq library
I know it is not quite what you are looking for, but index on [DataOwnerID] with inclusion of DataTimeStamp could make your query less expensive.

Implementing a "like" operator multiple times in one Linq to Entities query

We have a list of strings and we need to filter our results by that list. Example would be find all students who have SSNs that start with 465, 496, or 497 (plus x more)
List<string> list = GetPossibleStartsWithValues();
var qry = from student in entities.Students.WhereStartsWith(x=>x.SSN, list)
select new SearchedStudent
{
Name = student.Name,
SSN = student.SSN,
...
}
The code provided here is close to what we need, but we can't figure out how to impliment the StartsWith that we need using the Expression Class.
Well, you could try this:
public static IQueryable<T> WhereStartsWith<T>(this IQueryable<T> source,
Expression<Func<T, string>> projection,
List<T> list)
{
return source.Where(x => list.Any(y => projection(x).StartsWith(y)));
}
That may well not work, but it would be worth trying before you go into anything more complicated.
EDIT: As you say, the above won't compile - you basically need to build an expression tree representing the bit within the Where clause. Oops. However, before you start doing that, it would be worth seeing whether it'll work in the end. Try this:
List<string> list = GetPossibleStartsWithValues();
var qry = from student in entities.Students
.Where(student => list.Any(y => student.SSN.StartsWith(y)))
select new SearchedStudent
{
Name = student.Name,
SSN = student.SSN,
...
}
If that doesn't work, then making a more general method won't be any use :(
How about using a compound statement such as
var qry = from student in entities.Students.Where(
s => list.Where( x => s.StartsWith(x)).Count() != 0 )

Categories

Resources