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.
I am attempting to order the results of a Linq query by the length of a property and then by the property itself in order to order a string as an integer but the generated SQL is not ordering as I would expect it to.
I am joining multiple tables, filtering it down, selecting a DTO out with:
query = basequery.Select(s => new HeadersDTO
{
headerid = s.Header.id,
orderno = s.Header.orderno,
customer = s.Header.customer,
dateoforder = s.Header.dateoforder,
consignee = s.Location.name,
city = s.Location.name,
state = s.Location.state
}).Distinct();
Then trying to order by s.Header.orderno
query = query.OrderByDescending(x => x.orderno.Length).ThenByDescending(x => x.orderno)
.Skip(() => offset).Take(() => criteria.per_page);
This still orders it the normal way strings are ordered with first character taking precedence.
But if I select the x.orderno.Length out into it's own property and then order by that it works e.g.
query = basequery.Select(s => new HeadersDTO
{
ordernolength = s.Header.orderno.Length <---- added this
headerid = s.Header.id,
orderno = s.Header.orderno,
customer = s.Header.customer,
dateoforder = s.Header.dateoforder,
consignee = s.Location.name,
city = s.Location.name,
state = s.Location.state
}).Distinct();
query = query.OrderByDescending(x => x.ordernolength).ThenByDescending(x => x.orderno)
.Skip(() => offset).Take(() => criteria.per_page);
Is there a way to do this where I don't have to create a new property in the select list? I can add more information if needed.
Try to create a custom Comparer using IComparer where you do the Int32 check for this field. Here is an example for this:
Use own IComparer<T> with Linq OrderBy
Hope this helps
In my ASP.NET MVC project I'm trying to achieve the equivalent of this SQL Server code:
SELECT COUNT(*) as TotalCount, SUM(SomeColumn) as TotalSum
FROM SomeTable
WHERE param1 = 'foo' AND param2 = 'bar'
...with Entity Framework in a single async query. Essentially I'm looking to get not only the sum, but also the number of rows used to calculate the sum of the SomeColumn column in a single call.
So far I've only managed to do this in two calls:
using (var context = new myEntities())
{
// 1st query which does .SumAsync(x => x.SomeColumn)
// 2nd query which performs a .Count() on the result set
}
Is it possible to do this in one async call, like the SQL provided above? Should I just create a view or stored proc on the SQL side?
var query = await (from zz in db.SomeTable
where zz.Foo > 1
group zz by 1 into grp
select new
{
rowCount = grp.CountAsync(),
rowSum = grp.SumAsync(x => x.SomeColumn)
}).ToListAsync();
I tested a sync version in a dev environment I am running.
Tested with EF Core 6.0
The following LINQ query:
await Table.GroupBy(_ => 1, (_, rows) => new
{
Count = rows.Count()
Sum = rows.Sum(x => x.Column),
Average = rows.Average(x => x.Column),
})
.FirstOrDefaultAsync();
Will be translated to the following SQL query:
SELECT TOP(1)
COUNT(*) AS [Count]
SUM([t].[Column]) AS [Sum],
AVG([t].[Column]) AS [Average],
FROM[Table] AS [t]
It's really not necessary to call SumAsync on the IQueryable. The following will give you the query you want in the way you want it:
var foo = from item in context.Items
group item by item.ID into grp
select new
{
Value = grp.Sum(f => f.ID),
Count = grp.Count()
};
await foo.ToListAsync();
Is there a way to write a query in LINQ to return the count of the search terms found in a field(s)
Basically, I want this to work:
var matches = from t in _db.Books
let score = GetScore(t, searchterms)
where score >= 1
orderby score descending
select t;
public static int GetScore(Book b, params string[] searchterms)
{
int count = 0;
foreach (string term in searchterms)
{
if (b.Title.Contains(term))
count++;
}
return count;
}
But, of course, that can't work.
Can my little GetScore function be translated into LINQ?
Thanks.
EDIT: I would also prefer to have the score accessible. Ideally I will be selecting my results into a SearchResults class (for the View) that would contain some Book info and the Book's score from the query. To update my query, it'd be something like this:
var matches = from t in _db.Books
let score = GetScore(t, searchterms)
where score >= 1
orderby score descending
select new SearchResult
{
Title = t.Title,
Type = "Book",
Link = "Books/Details/" + t.BookID,
Score = score
};
I'm sorry I wasn't more clear originally.
You can't do what you want to do without issuing multiple queries to the database - essentially one per search term. If you are happy to do that, then here is an easy way to do it:
var terms = new [] { "s", "t", "r", "e", "b", "c", };
var ids =
from term in terms
from id in _db.Books
.Where(book => book.Title.Contains(term))
.Select(book => book.Id)
group term by id into gts
orderby gts.Count() descending
select gts.Key;
var selectedIds = ids.Take(50).ToArray();
var query =
from book in _db.Books
where selectedIds.Contains(book.Id)
select book;
I wrote the ids to return a list of ids sorted by those that match the most terms first. This was to most closely get the same kind of result that you wanted in your question. I then decided to use a Take(50) to get the top 50 results. You can obviously change this strategy to suit your needs, but you must end up with an array of ids to use in the final query.
I hope this helps.
EDIT: based on OP's edit.
Here's how to query with the score included:
var terms = new [] { "s", "t", "r", "e", "b", "c", "l", "i", };
var idScores =
from term in terms
from id in _db.Books
.Where(book => book.Title.Contains(term))
.Select(book => book.BookID)
group term by id into gts
select new
{
Id = gts.Key,
Score = gts.Count(),
};
var selectedIds = idScores.Select(x => x.Id).Take(50).ToArray();
var selectedBooks =
from book in _db.Books
where selectedIds.Contains(book.BookID)
select book;
var query =
from b in selectedBooks.ToArray()
join x in idScores on b.BookID equals x.Id
orderby x.Score descending
select new
{
Title = b.Title,
Type = "Book",
Link = "Books/Details/" + b.BookID,
Score = x.Score,
};
If you want to convert your GetScore() function in LINQ then your can change whole LINQ to this:
var matches = from t in _db.Books
where searchterms.Count(c => c == t.Title) >= 1
orderby searchterms.Count(c => c == t.Title)
select t;
Now it will compile successfully but on run time when you will bind this matches to grid or any where it will throw exception "Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."
Because "problem is that we are trying to join an SQL table and an in-
memory list. Since you write the query against the SQL table, it goes
through LINQ to SQL, which rightly complains that it cannot do that.
so If you really want to do an in-memory join, then you must use _db.Books.AsEnumerable()
then query will be:
var matches = from t in _db.Books.AsEnumerable()
where searchterms.Count(c => c == t.Title) >= 1
orderby searchterms.Count(c => c == t.Title)
select t;
You have two options:
Do the search in the DB via a Stored procedure that returns a pair (BookId, Score), and then use that to do the query in LINQ2SQL
Use ToList() to execute the query, and avoid getting the "Local sequence cannot be used..." error.
For the second option, the query (in lambda syntax) would be something like
db.Books
.ToList()
.Select(t=> new SearchResult {
Title = t.Title,
Type = "Book",
Link = "Books/Details/" + t.BookID,
Score = GetScore(t, searchTerms)
})
.Where(t => t.Score >=1);
In the latter case, you'll be bringing the whole Book table to memory (and using LINQ2Objects to do the filtering), so I'd rather go for the first one.
if you want to get filtered data tou may build Expression by your self. I make some examle for your task, but you need to improve it for your source, or create some global improvements
let's start from the end. I can't make Expression returned anonymous Type so I create Generic wich I am planning to use as result
public class GRes<T>
{
public T Key { get; set; }
public int Count { get; set; }
}
I'm going to make grouping by any field of object or the object itself, and then on this grouping results call Sum fnction with some lambda which look's like this
x=>0 + IIF(x.Code.Contains(p1), 1, 0) + IIF(x.Code.Contains(p2), 1, 0) ...
where Code - is some field and p1,p2... - your search terms
at last call would be
IQueriable<GRes<Book>> result = context.Books
.GroupBy(d => d).CountIn(searchTerms, "Code")
.Where(r => r.Count > 0)
.OrderByDescending(r => r.Count);
CountIn - is extention :
public static IQueryable<GRes<TKey>> CountIn<TKey, TValue>(this IQueryable<IGrouping<TKey, TValue>> source, IEnumerable<string> values, Expression<Func<TValue,string>> selector)
{
ParameterExpression xExpr = selector.Parameters[0];
Expression propExpr = selector.Body;
MethodInfo mi = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
Expression res = Expression.Constant(0);
foreach (string term in values)
{
Expression value = Expression.Constant(term);
MethodCallExpression methodEpr = Expression.Call(propExpr, mi,value);
Expression tx = Expression.Condition(methodEpr, Expression.Constant(1), Expression.Constant(0));
res = Expression.Add(res, tx);
}
var r0 = Expression.Lambda<Func<pp_Disease, int>>(res, xExpr);
Type groupingType = typeof(IGrouping<TKey, TValue>);
ParameterExpression selPar = Expression.Parameter(groupingType, "i");
MethodInfo mi1 = typeof(Enumerable).GetMethods()
.FirstOrDefault(m => m.Name == "Sum"
&& m.ReturnParameter.ParameterType == typeof(int)
&& m.GetParameters().Count() == 2)
.MakeGenericMethod(typeof(pp_Disease));
Expression r1 = Expression.MemberInit(Expression.New(typeof(GRes<TKey>))
, Expression.Bind(typeof(GRes<TKey>).GetMember("Count")[0], Expression.Call(mi1, selPar, r0))
, Expression.Bind(typeof(GRes<TKey>).GetMember("Key")[0], Expression.Property(selPar, "Key")));
return source.Select(Expression.Lambda<Func<IGrouping<TKey, TValue>, GRes<TKey>>>(r1, selPar));
}
and when this function would be called your get SQL like this:
SELECT
...
FROM ( SELECT
...
FROM ( SELECT
....
SUM([Extent1].[A1]) AS [A1]
FROM ( SELECT
...
0 + (CASE WHEN ([Extent1].[Code] LIKE N'%2%') THEN 1 ELSE 0 END) + (CASE WHEN ([Extent1].[Code] LIKE N'%I%') THEN 1 ELSE 0 END) AS [A1]
FROM (SELECT
...
FROM [dbo].[pp_Disease] AS [pp_Disease]) AS [Extent1]
) AS [Extent1]
GROUP BY [K1], [K2], [K3], [K4]
) AS [GroupBy1]
WHERE [GroupBy1].[A1] > 0
) AS [Project1]
ORDER BY [Project1].[C1] DESC
i removed some fields declarations (EF generated sql huge), most important for this example is case string with parameters that we put in our function
i took your problem as below
books.Add("Robin HOOD");
books.Add("Charles");
books.Add("James");
search.Add("Rob");
search.Add("ood");
search.Add("les");
search.Add("am");
so you want the count of search items which are found in any of the books so if run this example , you will get the correct result.
var temp = searchterms.Where(x=>books.Where(y=>y.Title.Contains(x)).Count()>0);
var matches = from t in _db.Books
let score = searchterms.Where(term => t.Title.Contains(term)).Count()
where score >= 1
orderby score descending
select t;
edit
if its linq to sql i think my solution is devide into 2 part
// just results in db
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains(t.Title.Contains)
select t;
// sort results in c#
var sorting = for entry in results
let score = searchterms.Where(term => entry.Title.Contains(term)).Count()
orderby score
select new {......};
edit
oooh sorry little mistake. on first query
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains(t.Title.Contains)
select t;
change to
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains("^" + t.Title + "^")
select t;
the reason i use this query is 'var results' will gives all the result from db which should be ok becus it's string.Contains. Then sort the result in next query.
I have an SQL table with the following design:
TableSchedule:
Id
Description
ImportedDate
I can import a large number of items and they will all have the same ImportedDate.
How can I write a LINQ query that grabs only the entries with the most recent ImportedDate?
var ResultSchedule =
from a in Db.TableSchedule
where a.ImportedDate == (Newest?)
Try This
var ResultSchedule =
from a in Db.TableSchedule
where a.ImportedDate == (from item in DB.TableSchedule Select item.ImportedDate).Max()
Try this:
Select * From Table
Where ImportedDate =
(Select max(ImportedDate) From Table)
If you need the latest record for each [something] like, say, for each individual customer, then make the siubquery correlated
Select * From Table t
Where ImportedDate =
(Select max(ImportedDate)
From Table
Where Customer = t.Customer)
How about this:
var ResultSchedule = from a in Db.TableSchedule
let max = Db.TableSchedule.Max(i => i.ImportedDate)
where a.ImportedDate == max
select a;
You can group the results and order them by date:
var ResultsSchedule = Db.TableSchedule.
GroupBy(a => a.ImportedDate).
OrderByDescending(g => g.Key).
FirstOrDefault().
Select(g => g);
In fluent syntax:
var ResultSchedule =
Db.TableSchedule.Where(
a => a.ImportedDate.Equals(Db.TableSchedule.Max(b => b.ImportedDate))
);