LINQ Query OrderByDescending not recognizing lambda expression - c#

I'm fairly new to LINQ, but I think I'm getting the hang of it.
I'm trying to group a select statement, then order it descending. I've got pretty far just looking through other questions, but when I try to attach the OrderByDescending() function, lambda expressions I try to add are not recognized by intellisense.
var test = db.UserPokemons.Where(x => x.PkmnDataId == pokemon.Id && x.StepsToHatch == 0)
.GroupBy(n => n.User1.Country).OrderByDescending();
So, for example, .OrderbyDescending(x => x.User1.Country) does not contain a definition for User1.
Is this to do with the ordering of my statements? I think it's because I'm calling GroupBy before OrderBy, but I can't wrap my head around how to fix it.
How can I order my groups?
Oh! Nearly forgot - I only want the top 3 countries, so is there an easy way to restrict that as well?
How to use GroupBy and OrderByDescending in the same LINQ function
How to select only the top/first 3 groups
Thanks for any help!

I think this is what you want:
var test = db.UserPokemons.Where(x => x.PkmnDataId == pokemon.Id && x.StepsToHatch == 0).
GroupBy(n => n.User1.Country).Select(x=>x.First()).OrderByDescending().Take(3);

var countriesInOrder = db.UserPokemons.Where(x => x.PkmnDataId == pokemon.Id && x.StepsToHatch == 0).
GroupBy(n => n.User1.Country).OrderByDescending(x => x.Key).Take(3).ToArray();
Got it - the lambda expression needs to go to the x.Key after grouping.
(Thanks jitender!)

Related

Combine two Lambda expressions using result of first Lambda expression

I was wondering if it's possible to combine these two statements into one.
Tbl_OrderFeeItem orderFee = adj.Tbl_Order.Tbl_OrderFeeItem.Single(x =>
x.OrderId == adj.OrderId
&& x.FeeTypeId == adj.FeeTypeId);
int querySource = orderFee.Tbl_FeeCheck
.Single(x => x.OrderFeeItemId == formFee.OrderFeeItemId)
.Tbl_PostPaymentOrderQuery
.PostPaymentOrderQueryTypeId;
What I'm trying to do is something like this...
int querySource = adj.Tbl_Order.Tbl_OrderFeeItem.Single(x =>
x.OrderId == adj.OrderId && x.FeeTypeId == adj.FeeTypeId)
.Tbl_FeeCheck.Single('use the id from the result of Tbl_OrderFeeItem.Single() call')
.Tbl_PostPaymentOrderQuery.PostPaymentOrderQueryTypeId;
I hope that made sense, I'm trying to use the id from the first query in the second but have it as one complete statement instead of two separate ones.
Thanks in advance
It's difficult to provide an accurate answer without any of the constructs present in your program, but by surrounding the original query with brackets it should allow you to perform the second query on the result of the first without using two seperate statements:
((Tbl_OrderFeeItem)adj.Tbl_Order.Tbl_OrderFeeItem.Single(x =>
x.OrderId == adj.OrderId
&& x.FeeTypeId == adj.FeeTypeId))
.Tbl_FeeCheck
.Single(x => x.OrderFeeItemId == formFee.OrderFeeItemId)
.Tbl_PostPaymentOrderQuery
.PostPaymentOrderQueryTypeId;

Why Reverse() cannot be converted into SQL?

My application is running under ASP.NET 4.0, which uses BLToolkti as ORM tool.
I have some queryable expression:
var q = db.GetTable<T>()
.Where(tb=>tb.TeamId==MyTeamId && tb.Season==MySeasonId)
.OrderByDescending(tb=>tb.Id)
.Take(20)
.Reverse()
Attempt to convert q.ToList() causes the following error:
Sequence 'Table(TeamBudget).Where(tb => ((tb.TeamId ==
value(VfmElita.DataLogicLayer.Teams.Team+TeamBudget+<>c__DisplayClass78).teamId)
AndAlso (tb.Season ==
value(VfmElita.DataLogicLayer.Teams.Team+TeamBudget+<>c__DisplayClass78).season))).OrderByDescending(tb
=> Convert(tb.Id)).Take(20).Reverse()' cannot be converted to SQL.
If I remove ".Reverse()" from the queryable object everything works fine.
What is the reason why queryable object with .Reverse() cannot be converted into SQL? Is that BLToolkit limitation? Is there any solution workaround for that?
Thank you!
It's pretty clear what the other LINQ methods convert to (where, order by, top(20)), but what would Reverse() convert to? I can't think of an SQL statement I've seen that mimics that behavior, and when you're querying the database your LINQ statement must ultimately resolve to valid SQL.
This may not be what you're going for, but one option would be to execute the query first using ToList(), then apply Reverse():
var q = db.GetTable<T>()
.Where(tb => tb.TeamId == MyTeamId && tb.Season == MySeasonId)
.OrderByDescending(tb => tb.Id)
.Take(20)
.ToList()
.Reverse();
Alternatively, you could get the count and skip that many records first, although this could be inaccurate if the number of records change between calls. Plus it's two queries instead of just one.
var totalRecords = db.GetTable<T>()
.Count(tb => tb.TeamId == MyTeamId && tb.Season == MySeasonId);
var q = db.GetTable<T>()
.Where(tb => tb.TeamId == MyTeamId && tb.Season == MySeasonId)
.Order(tb => tb.Id)
.Skip(totalRecords)
.Take(20);

EF: LINQ - orderby using child collection with condition - ArgumentException

I'm running into troubles trying to sort IQueryable of my EF Entity.
My object structure is something like this:
Item
Item.CustomFieldValue [List<CustomFieldValue>]
Item.CustomFieldValue.DefinitionID
Item.CustomFieldValue.Value
and I'm working with
IQueryable<Item>
I'd need to sort it conditionally with values having desired definition id being sorted first something like this:
queryable = queryable
.OrderBy(p => p.CustomFieldValue
.Where(p2 => p2.DefinitionID == defId)
.Select(p3 => p3.Value)
.OrderBy(p4 => p4)
);
This however throws ArgumentException "DbSortClause expressions must have a type that is order comparable.".
I indeed understand what's the exception trying to say to me, I just can't figure out on how to change this so that valid query is generated.
Any help greatly appreciated
EDIT:
To bring some more light into the issue, I want to achieve something similar that this query does
SELECT * FROM ticketnumber t, customfieldvalue c
WHERE t.id like '%00000047%' and c.ticketnumberid = t.id
ORDER BY CASE
WHEN DefinitionId = 2125 THEN 1
ELSE 2
END, c.Value ASC
Alternatively, as time is starting to become a factor for me, is there a way I could append OrderBy in string form?
You probably want to use FirstOrDefault() at the end of the end of the first OrderBy so you won't be dealing with enumerables but with values.
queryable = queryable
.OrderBy(p => p.CustomFieldValue
.Where(p2 => p2.DefinitionID == defId)
.Select(p3 => p3.Value)
.OrderBy(p4 => p4)
.FirstOrDefault()
);
Modification of Joanvo's answer did the trick, this is the working code [I've removed the inner OrderBy]
queryable = queryable.OrderBy(p => p.CustomFieldValue.Where(p2 => p2.DefinitionID == defId).Select(p3 => p3.Value).FirstOrDefault());

Why is LINQ not letting me OrderBy one of my data points?

I have this code:
return inventoryItems
.Where(i => 0 < String.Compare(i.ID, ID))
.Take(CountToFetch);
...but I want to order the results, like so:
return inventoryItems
.Where(i => 0 < String.Compare(i.ID, ID))
.Take(CountToFetch)
.OrderBy(i.pksize);
...however, the final i is red/out of scope. Why? Trying to position the OrderBy() prior to the Take() makes no difference.
return inventoryItems.Where(i => 0 < String.Compare(i.ID, ID))
.Take(CountToFetch)
.OrderBy(i => i.pksize);
And maybe you should change OrderBy and Take order to make results predictable:
return inventoryItems.Where(i => 0 < String.Compare(i.ID, ID))
.OrderBy(i => i.pksize)
.Take(CountToFetch);
There's no i in the OrderBy.
You want OrderBy(i => i.pksize) (or indeed x => x.pksize, whatever => whatever.pksize, etc.)
The sort of syntax that allows from x in something where x.IsOkay orderby x.Priority etc. uses the same variable label all the way through, but it gets turned into more than one lambda expression, which are each separate from each other. (something.Where(x => x.IsOkay).OrderBy(x => x.Priority), but they need to each be full expressions.
It looks like you've tried to use the i you declared in Where. If you look closely at the brackets, you should see that it is actually no longer in scope within the OrderBy. So the first problem is you're trying to use an out-of-scope variable.
OrderBy actually needs to be able to check the property on each element in the IEnumerable, so it doesn't make sense to only pass it the value of one element's property. It needs to know how to get the property for each element, which is why you typically pass in a lambda expression, anonymous delegate or method.
As the others pointed out, you can solve the problem by using an lambda expression like you did in Where:
.OrderBy(i => i.pksize)

and operator in Linq and select distinct values using linq

I am new to .net. I have a form in which there are two comboboxes cbProduct and cbBrandName and also a label lblPrice.
I am trying to implement the below code but it is showing blue scribbles to &&.
(Error: operator '&&' cannot be applied to operands of type 'lambda expression' and 'lambda expression')
I tried the below code: (not working)
lblPrice.Text = string.Empty;
lblPrice.Text = doc.Descendants("items"
).Where((x => x.Element("productname"
).Value.Equals(cbProduct.SelectedItem.ToString())) && /*blue scribbles to '&&'*/
(y => y.Element("brandname").Value.Equals(cbBrandName.SelectedItem.ToString()
))).Select(k => k.Element("price"
).Value).ToString();
My other question is that i want to make the selected values of cbProduct as distinct. The below code takes all the values instead of distinct values:
cbProduct.Items.AddRange(doc.Descendants("items"
).Select(x => x.Element("productname").Value
).ToArray<string>());//adds all products
cbProduct.SelectedIndex = 0;
giving any one answer is ok
Please assist me
Thanks in advance
It looks like you are passing 2 lambdas to the Where function and trying to logical-and (&&) them together. You can't do that. The && has to occur inside the Where lambda. Or you can chain 2 Where functions together. Something like this:
lblPrice.Text = doc.Descendants("items")
.Where(x => x.Element("productname").Value.Equals(cbProduct.SelectedItem.ToString()) &&
x.Element("brandname").Value.Equals(cbBrandName.SelectedItem.ToString()))
.Select(k => k.Element("price").Value).ToString();
The other issue I see is you are ending your query with a select, but never actually enumerating it. You probably want to do something like this:
lblPrice.Text = doc.Descendants("items")
.Where(x => x.Element("productname").Value.Equals(cbProduct.SelectedItem.ToString()) &&
x.Element("brandname").Value.Equals(cbBrandName.SelectedItem.ToString()))
.Select(k => k.Element("price").Value)
.FirstOrDefault();
Which will return the string you are looking for, or null if nothing exists (so you probably want to skip the final .ToString() call in this case, since you are already returning a string from Select and .ToString() on a null will throw an exception).
For the first question, it looks like you just want to select the one price. This code will work, assuming that the item is found by the .Single(). It will throw otherwise, in which case you should use .SingleOrDefault() and check for null on the found item.
lblPrice.Text =
doc.Descendants("items")
.Single(x => x.Element("productname").Value == cbProduct.SelectedItem.ToString() &&
x.Element("brandname").Value == cbBrandName.SelectedItem.ToString())
.Element("price").Value;
For the second question, you need to close off your .Select with a bracket, then you can call .Distinct() and .ToArray() to filter to distincts and project the result to string[]. I've also thrown an .OrderBy() in there, as there's nothing more annoying than a ComboBox in a random order. Try this:
cbProduct.Items.AddRange(doc.Descendants("items")
.Select(item => item.Element("productname").Value)
.Distinct()
.OrderBy(item => item)
.ToArray());

Categories

Resources