LINQ How to use multiple ThenBy dynamically? - c#

I didn't think this would be so rare to find but as it seems, it is. Here's the situation.
I've a ApplySort method which takes in 3 parameters posted from a mvc page.
private List<dynamic> ApplySort(List<dynamic> listToBeSorted, string sortBy, string sortOrder)
{
if (String.IsNullOrEmpty(sortBy))
sortBy = "createddate";
if (String.IsNullOrEmpty(sortOrder) || sortOrder.Trim() != "0")
sortOrder = "1"; // 1 = descending, 0 = ascending
if (sortOrder == "1")
{
switch (sortBy)
{
case "name":
listToBeSorted = listToBeSorted.OrderByDescending(a => a.name).ToList();
break;
case "assigned":
listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
break;
case "duedate":
listToBeSorted = listToBeSorted.OrderByDescending(a => a.end).ToList();
break;
case "status":
listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
break;
default:
listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
break;
}
}
else
{
// same code as in if-block, with just OrderBy calls instead of OrderByDescending
}
return listToBeSorted;
}
Two problems:
1) Method seems unnecessarily long (with very similar code inside if and else blocks).
2) I want to be able to sort using multiple columns. sortBy param can have values like "name,title,createddate,status". So the sort applied should be, first by name, thenby title, then by createddate...and so on. I can use ThenBy by checking params sequentially. But how to dynamically apply a chain of ThenBy(s) based on the param value, where the number of ThenBy can vary.
string[] sortParams = sortBy.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
listToBeSorted.OrderBy(i=>i.sortParams[0]).ThenBy(j=>j.sortParams[1]).ThenBy(k=>k.sortParams[2])...(so on till sortParams.length)
How would I do this ? Plus, also how can I use the sortOrder parameter to sort by ascending or descending in the same line instead of using if-else.

You can create dictionary with Func<string, IComparable> mapping to properties of your class.
public class ItemWithProperty
{
public string Property { get; set; }
}
public static void Main()
{
Dictionary<string, Func<ItemWithProperty, IComparable>> stringPropertyMap = new Dictionary<string, Func<ItemWithProperty, IComparable>>()
{
{"param1", item => item.Property}
};
List<ItemWithProperty> toBeOrdered = new List<ItemWithProperty>();
string[] parameters = {"param1"};
var sorted = toBeOrdered.OrderBy(stringPropertyMap[parameters[0]]);
}

You use this if you change your method signature:
private static IEnumerable<dynamic> ApplySort(IEnumerable<dynamic> listToBeSorted, ICollection<KeyValuePair<string, string>> sorters)
{
var orderBy = (sorters == null || sorters.Count == 0) ? new KeyValuePair<string, string>("createddate", "1") : sorters.First();
var thenBys = (sorters == null || sorters.Count == 1) ? new List<KeyValuePair<string, string>>() : sorters.Except(Enumerable.Repeat(orderBy, 1));
var orderedEnumerable = orderBy.Value == "1"
? listToBeSorted.OrderBy(x => GetPropertyValue(x, orderBy.Key))
: listToBeSorted.OrderByDescending(x => GetPropertyValue(x, orderBy.Key));
orderedEnumerable = thenBys.Aggregate(orderedEnumerable, (current, thenBy) => thenBy.Value == "1"
? current.ThenBy(x => GetPropertyValue(x, thenBy.Key))
: current.ThenByDescending(x => GetPropertyValue(x, thenBy.Key)));
return orderedEnumerable.ToList();
}
private static object GetPropertyValue(dynamic obj, string propName)
{
Type t = obj.GetType();
return t.GetProperty(propName).GetValue(obj, null);
}
Try :
static void Main(string[] args)
{
var list = new List<dynamic>();
list.Add(new { name = "Billy" });
list.Add(new { name = "Johnny" });
list.Add(new { name = "Ali" });
var list2 = ApplySort(list, new List<KeyValuePair<string, string>>(new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("name", "1") }));
foreach (var o in list2)
{
Console.WriteLine(o.name);
}
Console.ReadLine();
}

Related

Update list by another list (linq)

I have List of object of class "Data" that look like:
class Data
{
int code;
string name;
...
DateTime date_update;
}
and I have another list of class, like:
class RefCodes
{
int old_code;
int new_code;
string new_name;
DateTime date_update;
}
The list of "Data" contains like 1,000 objects.
The list of "RefCodes" contains like 30 objects.
I need to replace in list "Data",
the fields:
"code" to be with value of "new_code",
and the "name" to be with value of "new_name".
The replacement need to be only for the objects that their code exist in list "RefCodes".
by the query: if code in Data.code == RefCodes.old_code
How can I do it?
I think you're looking for this:
foreach (var rcodeObj in RefCode)
{
foreach(var obj in (Data.Where(t => t.code == rcodeObj.old_code)))
{
obj.code = rcodeObj.new_code;
obj.name = rcodeObj.new_name;
}
}
If you are using C#6 you could use linq to do something like this
var updatedData = data.Select(x => new Data
{
code = refCodes.FirstOrDefault(y => y.old_code == x.code)?.new_code ?? x.code,
name = refCodes.FirstOrDefault(y => y.old_code == x.code)?.new_name ?? x.name,
});
You can use the following code:
foreach (var x in DataList)
{
var itemRefCode = RefCodesList.FirstOrDefault(d => d.old_code == x.code);
if (itemRefCode != null)
{
x.code = itemRefCode.new_code;
x.name = itemRefCode.new_name;
}
}
You can iterate through each of the lists and update the values as follows. Here I am using some sample inputs as shown below. Note that I am considering the fields of the classes to be public, for simplicity:
List<Data> dataList = new List<Data>
{
new Data { code = 1, name = "A" },
new Data { code = 2, name = "B" },
new Data { code = 10, name = "C" },
};
List<RefCodes> refList = new List<RefCodes>
{
new RefCodes { old_code = 1, new_code = 11, new_name = "X" },
new RefCodes { old_code = 2, new_code = 22, new_name = "Y" }
};
Console.WriteLine("Before");
dataList.ForEach(data => Console.WriteLine(data.code + ": " + data.name));
Console.WriteLine("");
Here is the code to do the updating:
foreach (var refCodes in refList)
{
foreach (var data in dataList)
{
if (data.code == refCodes.old_code)
{
data.code = refCodes.new_code;
data.name = refCodes.new_name;
}
}
}
Console.WriteLine("After");
dataList.ForEach(data => Console.WriteLine(data.code + ": " + data.name));
Output:
Before
1: A
2: B
10: C
After
11: X
22: Y
10: C
Would this solve your problem:
public void Update( List<Data> data, List<RefCodes> refCodes )
{
List<RefCodes> differences = refCodes
.Where( r => data.Any( d => r.old_code == d.code ) )
.ToList();
differences.ForEach( ( RefCodes item ) =>
{
Data element = data.FirstOrDefault( d => d.code == item.old_code );
element.code = item.new_code;
element.name = item.new_name;
} );
}
What you need is a Left Outer Join.
For example,
IEnumerable<Data> query = from data in dataList
join refCode in refList on data.code equals refCode.old_code into joined
from subCode in joined.DefaultIfEmpty()
select new Data
{
code = subCode?.new_code ?? data.code,
name = subCode?.new_name ?? data.name,
date_update = subCode == null ? data.date_update : DateTime.Now
};
will return a sequence with the result you expect.
**Let say tempAllocationR is list 1 and tempAllocationV is List2 **
var tempAllocation = new List<Object>();
if (tempAllocationR.Count > 0 && tempAllocationV.Count > 0)
{
foreach (TempAllocation tv in tempAllocationV)
{
var rec = tempAllocationR.FirstOrDefault(tr => tr.TERR_ID == tv.TERR_ID && tr.TERR == tv.TERR && tr.Team == tv.Team);
if (rec != null)
{
rec.Vyzulta = tv.Vyzulta;
}
else
{
tempAllocationR.Add(tv);
}
}
tempAllocation = tempAllocationR;
}
else if (tempAllocationV.Count == 0 && tempAllocationR.Count > 0)
{
tempAllocation = tempAllocationR;
}
else if (tempAllocationR.Count == 0 && tempAllocationV.Count > 0)
{
tempAllocation = tempAllocationV;
}

Lambda expression or Linq to fetch filtered result out of keyvaluepair from list?

I don't know how to write expression/query to fetch the result from 2 level deep List containing KeyValuePair<object, object>
For example:
IList<ITaskData> taskDataList //1st Level
here IList contains collection of ITaskData and ITaskData contains
IList<KeyValuePair<object, object>> TaskParams { get; set; } //2nd Level
So suppose TaskParams have below key value pairs
Key : Location
Values: Stockroom, Salesfloor
Key : Iteration
Values : 1, 2
So, I need to fetch the List of TaskData which contains TaskParams values Stockroom and 1.
I can do easily by foreach loop but I wanted to use Linq / Lambda which is one liner and more easily maintainable.
Thanks a lot for support. Please let me know if you need more clarification.
Working code by foreach loop: I am getting desire output in taskDataListType1
IList<ITaskData> taskDataListType1 = new List<ITaskData>();
IList<KeyValuePair<object, object>> taskParams = null;
bool iteration = false;
bool location = false;
foreach (ITaskData taskData in taskDataList)
{
taskParams = taskData.TaskParams;
if (taskParams != null)
{
foreach (KeyValuePair<object, object> keyValuePair in taskParams)
{
if (keyValuePair.Key.ToString().Equals("ITERATION", StringComparison.OrdinalIgnoreCase))
{
if (int.Parse(keyValuePair.Value.ToString()) == 1)
{
iteration = true;
}
}
else if (keyValuePair.Key.ToString().Equals("LOCATION", StringComparison.OrdinalIgnoreCase))
{
if (keyValuePair.Value.ToString() == "StockRoom")
{
location = true;
}
}
if (iteration == true && location == true)
{
taskDataListType1.Add(taskData);
}
}
}
}
Strange but if I put below logic its not working I mean I am not getting any values in tasks1
IList<ITaskData> taskDataListType1 = new List<ITaskData>();
foreach (TaskData td in taskDataList)
{
var tasks1 = taskParams.Where(kvp => kvp.Key != null
&& kvp.Value != null
&& kvp.Key.ToString().Equals("LOCATION", StringComparison.OrdinalIgnoreCase)
&& kvp.Value.ToString() == "StockRoom"
&& kvp.Key.ToString().Equals("ITERATION", StringComparison.OrdinalIgnoreCase)
&& int.Parse(kvp.Value.ToString()) == 1
);
}
Output of above query is screenshot below:
If you insist on using object in KeyValuePair, then your example would look like this:
IList<ITaskData> taskDataList = new List<ITaskData>
{
new ITaskData
{
TaskParams = new List<KeyValuePair<object,object>>
{
new KeyValuePair<object, object>("Location", "Stockroom"),
new KeyValuePair<object, object>("Location", "Salesfloor"),
new KeyValuePair<object, object>("Iteration", 1),
new KeyValuePair<object, object>("Iteration", 2)
}
},
new ITaskData
{
TaskParams = new List<KeyValuePair<object,object>>
{
new KeyValuePair<object, object>("Location", "Stockroom"),
new KeyValuePair<object, object>("Location", "Salesfloor"),
new KeyValuePair<object, object>("Iteration", 101),
new KeyValuePair<object, object>("Iteration", 2)
}
}
};
var result = taskDataList.Where(td =>
td.TaskParams.Any(tp => ((string)tp.Key == "Location") && ((string)tp.Value == "Stockroom")) &&
td.TaskParams.Any(tp => (string)tp.Key == "Iteration" && (int)tp.Value == 1)
);
As you can see, you need to cast object to an exact type, so this approach is very error-prone, and can easily cause run-time exceptions if you key,value collection will have items with type different from what you expect.
If you need to filter by location or iteration, define them as properties inside your TaskParams class, then your query will become more clear, strongly typed and less error-prone. See the example below:
public class TaskParamsType
{
public IList<string> Locations;
public IList<int> Iterations;
}
public class ITaskDataNew
{
public TaskParamsType TaskParams { get; set; }
}
var result = taskDataList.Where(td =>
td.TaskParams.Locations.Contains("Stockroom") &&
td.TaskParams.Iterations.Contains(1)
);
Try this:
var results =
taskDataList
.Where(td => td.TaskParams != null)
.Where(td =>
td.TaskParams.Any(kvp =>
kvp.Key != null
&& kvp.Key.ToString().Equals("LOCATION", StringComparison.OrdinalIgnoreCase)
&& kvp.Value != null
&& kvp.Value.Equals("Stockroom"))
&& td.TaskParams.Any(kvp =>
kvp.Key != null
&& kvp.Key.ToString().Equals("ITERATION", StringComparison.OrdinalIgnoreCase)
&& kvp.Value != null
&& kvp.Value.Equals(1)))
.ToList();
I have tested this code against this data:
IList<ITaskData> taskDataList = new List<ITaskData>();
var taskData = new TaskData();
taskData.TaskParams.Add(new KeyValuePair<object, object>("Location", "Stockroom"));
taskData.TaskParams.Add(new KeyValuePair<object, object>("Location", "Salesfloor"));
taskData.TaskParams.Add(new KeyValuePair<object, object>("Iteration", 1));
taskData.TaskParams.Add(new KeyValuePair<object, object>("Iteration", 2));
taskDataList.Add(taskData);
Let's suppose you have the following code which returns a List<KeyValuePair<object, object>> matching the logical condition:
public class ITaskData
{
public List<KeyValuePair<object, object>> keyValuePairs { get; set; }
}
class Program
{
private static List<ITaskData> list = new List<ITaskData>();
private static void Main()
{
List<KeyValuePair<object, object>> result = new List<KeyValuePair<object, object>>();
foreach (var a in list)
foreach (var b in a.keyValuePairs)
if (b.Value.ToString().Contains("Stockroom")) result.Add(b);
// Here I make .ToString().Contains("Stockroom")
// You can add any required logics here
}
}
You can make it in LINQ:
List<KeyValuePair<object, object>> result =
(from a in list
from b in a.keyValuePairs
where b.Value.ToString().Contains("Stockroom")
select b)
.ToList();
Or in LINQ method chain:
List<KeyValuePair<object, object>> result =
(list
.SelectMany(a => a.keyValuePairs, (a, b) => new {a, b})
.Where(t => t.b.Value.ToString().Contains("Stockroom"))
.Select(t => t.b)
).ToList();
However, in my private opinion, in your case the solution with foreachs looks more elegant and readable.
Of course, this code will throw a NullReferenceException as keyValuePairs are not initialized. I don't initialize it as it is an example and you have your own ITaskData class with proper initialization.
It should be something like this:
var tasks = taskDataList.Where(
i => i.TaskParams.Any(x => x.Key == "Location" && x.Value.Contains("Stockroom")) &&
i.TaskParams.Any(x => x.Key == "Iteration" && x.Values.Contains(2)));
The above code is just to explain the logic. You need to cast the object to the right type (if you know them) or user another comparison method.

Dynamic Linq Contains to filter a List

I have this Method
public static IEnumerable<T> Filter<T>(IEnumerable<T> source, string searchStr)
{
var propsToCheck = typeof(T).GetProperties().Where(a => a.PropertyType == typeof(string));
var filter = propsToCheck.Aggregate(string.Empty, (s, p) => (s == string.Empty ? string.Empty : string.Format("{0} OR ", s)) + string.Format("{0} == #0", p.Name).ToLower());
var filtered = source.AsQueryable().Where(filter, searchStr);
return filtered;
}
Which takes List and a search string and finds any matches in the list. However this only works for 100% matches, how can I make this case insensitive and use contains instead of a 100% match ?
Constructing a dynamic LINQ query doesn't look like the best option here. Filtering with a delegate would do better:
public static IEnumerable<T> Filter<T>(IEnumerable<T> source, string searchStr)
{
var searchStrLower = searchStr.ToLower();
var propsToCheck = typeof(T).GetProperties().Where(a => a.PropertyType == typeof(string) && a.CanRead);
return source.Where(obj => {
foreach (PropertyInfo prop in propsToCheck)
{
string value = (string)prop.GetValue(obj);
if (value != null && value.ToLower().Contains(searchStrLower)) return true;
}
return false;
});
}

Linq Conditional .Any() Select

How can I perform a conditional select on a column value, where I have a preference over which value is returned. If I can't find the top choice, I settle on the next, if available, and then if not the next, etc. As it looks right now, it would take 3 total queries. Is there a way to simplify this further?
var myResult = string.Empty;
if (myTable.Where(x => x.ColumnValue == "Three").Any())
{
myResult = "Three"; // Can also be some list.First().Select(x => x.ColumnValue) if that makes it easier;
}
else if (myTable.Where(x => x.ColumnValue == "One").Any())
{
myResult = "One";
}
else if (myTable.Where(x => x.ColumnValue == "Two").Any())
{
myResult = "Two";
}
else
{
myResult = "Four";
}
You could use a string[] for your preferences:
string[] prefs = new[]{ "One", "Two", "Three" };
string myResult = prefs.FirstOrDefault(p => myTable.Any(x => x.ColumnValue == p));
if(myResult == null) myResult = "Four";
Edit Enumerable.Join is a very efficient hash table method, it also needs only one query:
string myResult = prefs.Select((pref, index) => new { pref, index })
.Join(myTable, xPref => xPref.pref, x => x.ColumnValue, (xPref, x) => new { xPref, x })
.OrderBy(x => x.xPref.index)
.Select(x => x.x.ColumnValue)
.DefaultIfEmpty("Four")
.First();
Demo
I wrote an extension method that effectively mirrors Tim Schmelter's answer (was testing this when he posted his update. :-()
public static T PreferredFirst<T>(this IEnumerable<T> data, IEnumerable<T> queryValues, T whenNone)
{
var matched = from d in data
join v in queryValues.Select((value,idx) => new {value, idx}) on d equals v.value
orderby v.idx
select new { d, v.idx };
var found = matched.FirstOrDefault();
return found != null ? found.d : whenNone;
}
// usage:
myResult = myTable.Select(x => x.ColumnValue)
.PreferredFirst(new [] {"Three", "One", "Two"}, "Four");
I've written one that will quit a little more early:
public static T PreferredFirst<T>(this IEnumerable<T> data, IList<T> orderBy, T whenNone)
{
// probably should consider a copy of orderBy if it can vary during runtime
var minIndex = int.MaxValue;
foreach(var d in data)
{
var idx = orderBy.IndexOf(d);
if (idx == 0) return d; // best case; quit now
if (idx > 0 && idx < minIndex) minIndex = idx;
}
// return the best found or "whenNone"
return minIndex == int.MaxValue ? whenNone : orderBy[minIndex];
}
I use a weighted approach in SQL where I assign a weight to each conditional value. The solution would then be found by finding the highest or lowest weight depending on your ordering scheme.
Below would be the equivalent LINQ query. Note that in this example I am assigning a lower weight a higher priority:
void Main()
{
// Assume below list is your dataset
var myList =new List<dynamic>(new []{
new {ColumnKey=1, ColumnValue ="Two"},
new {ColumnKey=2, ColumnValue ="Nine"},
new {ColumnKey=3, ColumnValue ="One"},
new {ColumnKey=4, ColumnValue ="Eight"}});
var result = myList.Select(p => new
{
ColVal = p.ColumnValue,
OrderKey = p.ColumnValue == "Three" ? 1 :
p.ColumnValue == "One" ? 2 :
p.ColumnValue == "Two" ? 3 : 4
}).Where(i=> i.OrderKey != 4)
.OrderBy(i=>i.OrderKey)
.Select(i=> i.ColVal)
.FirstOrDefault();
Console.WriteLine(result ?? "Four");
}
How about something like this:
var results = myTable.GroupBy(x => x.ColumnValue).ToList();
if (results.Contains("Three")) {
myResult = "Three";
} else if (results.Contains("One")) {
myResult = "One";
} else if (results.Contains("Two")) {
myResult = "Two";
} else {
myResult = "Four";
}

Create Order by programming Linq/Lambda

I have this code :
public void CreateOrdering(string field, string direction)
{
//direction : ASC/DESC
var result = context.MyTable
.Where(x => x.Code > 5)
.OrderBy()
.Skip(10)
.Take(5)
.ToList<MyTable>();
}
I rephrase, I have a method, this method receive as string field name for ordering and the direction ("ASC", "DESC")
I'd like create a Order with the field and the direction received in argument. I have to be able to :
I'd like in this Query be able to do an ascending and descending
Set the ordering field by programming, here Code may be later Id or other ...
The ordering must be done on the SQL Server side not on the list returned
Thanks,
You may use reflection in an extension method which allows for linq syntax:
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string direction)
{
string orderByMethod = (direction == "ASC") ? "OrderBy" : (direction == "DESC" ? "OrderByDescending" : null);
if(orderByMethod == null) throw new ArgumentException();
var propertyInfo = typeof (TSource).GetProperty(field);
var entityParam = Expression.Parameter(typeof(TSource), "e");
Expression columnExpr = Expression.Property(entityParam, propertyInfo);
LambdaExpression columnLambda = Expression.Lambda(columnExpr, entityParam);
MethodInfo orderByGeneric = typeof (Queryable).GetMethods().Single(m => m.Name == orderByMethod
&& m.GetParameters().Count() == 2
&& m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>));
MethodInfo orderBy = orderByGeneric.MakeGenericMethod(new [] {typeof(TSource), propertyInfo.PropertyType});
return (IQueryable<TSource>) orderBy.Invoke(null, new object[] { source, columnLambda });
}
Sample use:
internal class SomeType
{
public string StringValue { get; set; }
}
IQueryable<SomeType> l = new List<SomeType>
{
new SomeType {StringValue = "bbbbb"},
new SomeType {StringValue = "cccc"},
new SomeType {StringValue = "aaaa"},
new SomeType {StringValue = "eeee"},
}.AsQueryable();
var asc = l.OrderBy("StringValue", "ASC");
var desc = l.OrderBy("StringValue", "DESC");
Or for your example:
context.MyTable
.Where(x => x.Code > 5)
.OrderBy(field, direction)
.Skip(10)
.Take(5)
.ToList<MyTable>();
I may have misunderstood your question, but can't you just do:
Ascending
.OrderBy(x => x.Property)
Descending
.OrderByDescending(x => x.Property)
Update
What you need is Dynamic LINQ. However, what you are trying to do it could get quite complicated. As a simple workaround you could do something like:
var result = context.MyTable
.Where(x => x.Code > 15);
if (direction == "ASC")
{
result = result.OrderBy(field);
}
else
{
result = result.OrderByDescending(field);
}
result = result.Skip(10)
.Take(5)
.ToList<MyTable>();
void Main() {
// Ascending by some other property
CreateOrdering(item => item.SomeProperty, SortDirection.Ascending).Dump("Ascending order for SomeClass.SomeProperty");
// Descending by some other property
CreateOrdering(item => item.SomeProperty, SortDirection.Descending).Dump("Descending order for SomeClass.SomeProperty");
// Ascending by the Code property
CreateOrdering(item => item.Code, SortDirection.Ascending).Dump("Ascending order for SomeClass.Code");
// Descending by the Code property
CreateOrdering(item => item.Code, SortDirection.Descending).Dump("Descending order for SomeClass.Code");
}
// I reccomend not using bare strings, and instead use an enum
public enum SortDirection {
Ascending = 0,
Descending = 1
}
// Define other methods and classes here
public List<SomeClass> CreateOrdering<T>(Expression<Func<SomeClass, T>> field, SortDirection direction) {
// query does not get executed yet, because we have not enumerated it.
var query = context.MyTable
.Where(x => x.Code > 5);
if (direction.Equals(SortDirection.Ascending)) {
query = query.OrderBy (field);
} else {
query = query.OrderByDescending (field);
}
// query gets executed when the call ToList is made.
return query.Skip(10)
.Take(5)
.ToList();
}
public static class context {
private static List<SomeClass> _MyTable = new List<SomeClass>() {
new SomeClass("A", 4), new SomeClass("B", 5), new SomeClass("C", 6),
new SomeClass("D", 7), new SomeClass("E", 8), new SomeClass("F", 9),
new SomeClass("G", 10), new SomeClass("H", 11), new SomeClass("I", 12),
new SomeClass("J", 13), new SomeClass("K", 14), new SomeClass("L", 15),
new SomeClass("M", 16), new SomeClass("N", 17), new SomeClass("O", 18)
};
public static IQueryable<SomeClass> MyTable {
get {
return _MyTable.AsQueryable();
}
}
}
public class SomeClass {
public SomeClass(string property, int code) {
this.SomeProperty = property;
this.Code = code;
}
public string SomeProperty { get; set; }
public int Code { get; set; }
}
normally you would do this:
.OrderBy(x => x.yourField)
or
.OrderByDescending(x => x.yourField)
if you need your field to be dynamic, check this answer
If the field is passed as a string (for instance when using an ObjectDataSource), you can map it using a switch:
var qry = context
.MyTable
.Where(x => x.Code > 5);
switch(orderBy) {
case "MyField": qry = qry.OrderBy(r => r.MyField); break;
case "MyField DESC": qry = qry.OrderByDescending(r => r.MyField); break;
}
// By the way, ToList can infer the generic type if you don't
// want to state it explicity
var result = qry.Skip(10).Take(5).ToList();
The query is not executed before the ToList, and at least with EF it is executed on the SQL Server. I admit the switch is quite a lot of boilerplate, but it did turn out to be quite reliable and fast.

Categories

Resources