score:2
To sort by an arbitrary property, you need to build an expression tree to pass to OrderBy
.
To sort by an arbitrary number of properties, you need to call ThenBy
in a loop.
score:9
You should be able to do something along these lines:
public IEnumerable<MyType> DoSomething(params Expression<Func<MyType,object>>[] properties)
{
var query = // create LINQ query that returns IQueryable<MyType>
query = query.OrderBy(properties.First());
foreach (var property in properties.Skip(1))
{
query = query.ThenBy(property);
}
}
…
var results = DoSomething(() => x.Age, () => x.Height, () => x.LastName);
You'd need to handle the case where fewer than 2 properties are specified.
score:3
Following on from Jay's answer, this can be made into a nice extension method:
public static class EnumerableExtensions
{
public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable,
params Expression<Func<T, object>>[] expressions)
{
if (expressions.Length == 1)
return enumerable.OrderBy(expressions[0].Compile());
var query = enumerable.OrderBy(expressions[0].Compile());
for (int i = 1; i < expressions.Length;i++)
{
query = query.ThenBy(expressions[i].Compile());
}
return query;
}
}
Usage becomes quite simple, given a test object:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
This is then possible:
var people = new Person[]
{
new Person() {Name = "John", Age = 40},
new Person() {Name = "John", Age = 20},
new Person() {Name = "Agnes", Age = 11}
};
foreach(var per in people.OrderByMany(x => x.Name, x => x.Age))
{
Console.WriteLine("{0} Age={1}",per.Name,per.Age);
}
Output:
Agnes Age=11
John Age=20
John Age=40
UPDATE
You could add another overload of the OrderByMany
method to support SortOrder as well, although it gets clunky rather quickly. Personally I'd just go for the syntax
var query = from p
in people
order by Name, Age descending;
However, for the record, in C#4 at least, I would accomplish the overload using an enum & tuple.
public enum SortOrder
{
Ascending,
Descending
}
and the extra overload:
public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable,
params Tuple<Expression<Func<T, object>>,SortOrder>[] expressions)
{
var query = (expressions[0].Item2 == SortOrder.Ascending)
? enumerable.OrderBy(expressions[0].Item1.Compile())
: enumerable.OrderByDescending(expressions[0].Item1.Compile());
for (int i = 1; i < expressions.Length; i++)
{
query = expressions[i].Item2 == SortOrder.Ascending
? query.ThenBy(expressions[i].Item1.Compile())
: query.ThenByDescending(expressions[i].Item1.Compile());
}
return query;
}
Usage becomes clumsy and hard to read:
foreach (var per in people.OrderByMany(
new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Age, SortOrder.Descending),
new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Name, SortOrder.Ascending)))
{
Console.WriteLine("{0} Age={1}", per.Name, per.Age);
}
score:0
I like Jamiec's idea but I hate using Tuples because the syntax is ugly. Therefore I built a small class that encapsulates the Tuple and exposes getters for the Item1 and Item2 properties with better variable names.
Also notice that I used a default sort order of ascending so you only need to specify a SortOrder if you want to sort in descending order.
public class SortExpression<T>
{
private Tuple<Expression<Func<T, object>>, SortOrder> tuple;
public SortExpression( Expression<Func<T, object>> expression, SortOrder order =SortOrder.Ascending )
{
tuple = new Tuple<Expression<Func<T,object>>, SortOrder>(expression, order);
}
public Expression<Func<T, object>> Expression {
get { return tuple.Item1; }
}
public SortOrder Order {
get { return tuple.Item2; }
}
}
In my specific application, I have a repository base class which takes an IQueryable and converts it to a ObservableCollection. In that method I use the SortExpression class:
public ObservableCollection<T> GetCollection(params SortExpression<T>[] sortExpressions) {
var list = new ObservableCollection<T>();
var query = FindAll();
if (!sortExpressions.Any()) {
query.ToList().ForEach(list.Add);
return list;
}
var ordered = (sortExpressions[0].Order == SortOrder.Ascending)
? query.OrderBy(sortExpressions[0].Expression.Compile())
: query.OrderByDescending(sortExpressions[0].Expression.Compile());
for (var i = 1; i < sortExpressions.Length; i++) {
ordered = sortExpressions[i].Order == SortOrder.Ascending
? ordered.ThenBy(sortExpressions[i].Expression.Compile())
: ordered.ThenByDescending(sortExpressions[i].Expression.Compile());
}
ordered.ToList().ForEach(list.Add);
return list;
}
Here is the method in use:
var repository = new ContactRepository(UnitOfWork);
return repository.GetCollection(
new SortExpression<Contact>(x => x.FirstName),
new SortExpression<Contact>(x => x.LastName));
Source: stackoverflow.com
Related Articles
- Linq handling variable number of OrderBy
- Is there any way to create a LINQ query as a variable without having the data source (yet)?
- LINQ Source Code Available
- multiple orderby in this linq code
- LINQ Lambda efficiency of code groupby orderby
- creating Linq to sqlite dbml from DbLinq source code
- Using LINQ to filter on a variable number of parameters in c#
- How can I check the number of calls to the database in LINQ query when using .NET Core and Code First?
- How to assign multiple LINQ Include() statements to a variable for code re-use?
- Why the extension method of where for LINQ in this code would print out a single number while it shouldn't print anything at all?
- source code for LINQ 101 samples
- Linq OrderBy with an expression variable
- Building a Linq to EF query to a variable list of keywords in a variable number of columns?
- Linq sum by variable number of monthS or weekS
- LINQ returning variable number of outputs for each input (point list to line list)
- Can I assign the result of a Linq query to the source variable of the same query?
- c# Linq or code to extract groups from a single list of source data
- How to assign LINQ Query to a variable and then use it later in the code
- LINQ to SQL variable number of columns for Select
- Run variable number of LINQ queries asynchronously
- Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
- LINQ Orderby Descending Query
- Convert string[] to int[] in one line of code using LINQ
- Code equivalent to the 'let' keyword in chained LINQ extension method calls
- LINQ OrderBy versus ThenBy
- Linq code to select one item
- How do I specify the Linq OrderBy argument dynamically?
- LINQ OrderBy with more than one field
- Linq Order by a specific number first then show all rest in order
- Limit Number of Results being returned in a List from Linq
- Aggregation on Data with C# or SQL
- Extract list where string is contained inside a list inside the list
- Converting SQL query to LINQ or LINQ fluent Syntax
- IndexOutOfRangeException was unhandled by user code
- Join operation not working mongo c# driver linq
- SQL to LINQ Tool
- .Net LINQ - Filter a dictionary using another dictionary
- How to use IGrouping enumeration in C#
- Group objects into fixed-sized collections
- Creating a drop down menu with DIctionary Item
- Why doesn't deferred execution cache iterative values?
- Deleting from a generic list
- How to check the user already offer the ride using LINQ in NET Core
- Linq updating a database, primary key
- Specified cast not valid calling stored procedure
- Simplifying a LINQ expression
- Proper use of DBNull
- How to filter list in asp.net/vb.net using linq
- Inner query inside Wcf Services query throws 'NotSupportedException'
- How can I group a List<K> by a List<T>, depending on the results of an expression<T, K>?