MVC3 website. EF4.1 Code First.
I am saving the sort column and direction off to session, so that when the user comes back to the page the grid is still in the same sort order.
I want to be able to specify how to sort the collection with the values I have saved like so.
this.Issues = this.db.ITIssues.Orderby(this.sort + " " + this.sortdir)...
Currently I have to use a switch statement and handle every different combination of fields + sort direction. Is there a better way?
switch (this.Sort)
{
case "ITApplication.Name":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.ITApplication.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.ITApplication.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
case "ITIssueType.Name":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.ITIssueType.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.ITIssueType.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
case "CurrentStatus.Name":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.CurrentStatus.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.CurrentStatus.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
case "CurrentAssignedTo.Fname":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.CurrentAssignedTo.Fname).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.CurrentAssignedTo.Fname).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
case "CreatedBy.Fname":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.CreatedBy.Fname).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.CreatedBy.Fname).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
case "CurrentPriority.Name":
if (this.SortDir == "ASC")
this.Issues = this.db.ITIssues.OrderBy(i => i.CurrentPriority.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
else
this.Issues = this.db.ITIssues.OrderByDescending(i => i.CurrentPriority.Name).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
default:
开发者_运维百科 this.Issues = this.db.ITIssues.OrderByDescending(i => i.ID).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
}
I've had to do something similar recently, what you are looking for is Dynamic Linq! Take a look at this:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
There seems to be two obstacles with your code, the first involves getting a property name from the string represented by this.Sort
. That's probably going to involve some reflection code. The second part involves shortening the amount of code needed to specify ascending sort vs descending sort.
You could do this is several ways. The first idea I thought of was to make an extension method for OrderBy which took in a bool SortDir argument. There are a few overloads for OrderBy allowing for Func<T,TKey>
KeySelectors and an IComparer<T>
. I'll show you the simple one with just a keySelector, but you may want to implement all of them for a fully generic solution
public static class IEnumerableExtentions
{
public static IEnumerable<T> OrderBy<T,TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, bool reverse)
{
return reverse ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
}
}
With this in place, you could write your code without all those if's
switch (this.Sort)
{
case "ITApplication.Name":
this.Issues = this.db.ITIssues.OrderBy(i => i.ITApplication.Name, this.SortDir == "ASC").Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
/*...*/
}
As a second solution to determining sort order, you pass in an IComparer
which uses SortDir. Here's the IComparer implementation you could use
class ReversableComparer : IComparer<IComparable>
{
private bool _reverse;
public ReversableComparer(bool reverse)
{
_reverse = reverse;
}
public int Compare(IComparable x, IComparable y)
{
return _reverse ? y.CompareTo(x) : x.CompareTo(y);
}
}
Then you could write your code
switch (this.Sort)
{
case "ITApplication.Name":
this.Issues = this.db.ITIssues.OrderBy(i => i.ITApplication.Name, new ReversableComparer(this.SortDir != "ASC")).Where(i => i.ITAppGroupID == this.ITAppGroupID);
break;
/*...*/
}
That's a bit more code to write, but its there if you need it.
As far as the property name/reflection goes, if you're using Linq as part of Linq to SQL, the dynamic linq link provided by @AlbertVo might be worth a look. If you're working more with Linq to Objects, then instead of i => i.ITApplication.Name
try playing with i => i.GetType().GetProperty(this.Sort).GetValue(i, null)
Of course you'll need System.Reflection
to make that work and it may be slower than your original solution.
I hope some of that helped. Cheers.
EDIT As a summary, these two techniques can be combined. Your final code might look like this:
public static class IEnumerableExtentions
{
public static IEnumerable<T> OrderBy<T,TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, bool reverse)
{
return reverse ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
}
}
void Main()
{
this.Issues = this.db.ITIssues
.OrderBy(i => i.GetType().GetProperty(this.Sort).GetValue(i, null),
this.SortDir == "ASC")
.Where(i => i.ITAppGroupID == this.ITAppGroupID);
}
精彩评论