2

My problem is repetitive code: a not so DRY switch statement.

So I have a table with 12 columns that can be ordered by descending or ascending on click. My current solution is the use of a switch statement that checks which column was clicked.

The sortable properties:

enter image description here

This is the page where if a user clicks on a head the table gets ordered:

enter image description here

The SortByColumn property comes in a string. SortAscending boolean comes in from a @Html.CheckBoxFor.

enter image description here

You see where this is going? I have 12 columns that can be ordered, so this switch can get very lengthy and unmaintainable. So my question is, is it possible to refactor this with reflection or in some other way?

Matt
  • 4,462
  • 5
  • 25
  • 35
brroshan
  • 1,640
  • 17
  • 19

4 Answers4

2

The OrderBy function works by letting you return the property it should sort on, it will be called foreach item in the list.

Instead of hardcoding it, we can use reflection instead:

public ActionResult Index(AgentReportViewModel vm)
{
    var b = Agent.GetAgents();
    vm.Agents = vm.SortAscending 
        ? b.OrderBy(x => GetValueByColumn(x, vm.SortByColumn))
        : b.OrderByDescending(x => GetValueByColumn(x, vm.SortByColumn));
    return View(vm);
}

public object GetValueByColumn<T>(T x, string columnStr)
{
    // Consider caching the property info, as this is a heavy call.
    var propertyInfo = x.GetType().GetProperty(columnStr);    
    return propertyInfo.GetValue(x, null);
}
André Snede
  • 9,899
  • 7
  • 43
  • 67
1

You can use expression trees for this scenario.

public static Func<Agent, object> GetOrderBySelector(string propertyName)
{
    var parameter = Expression.Parameter(typeof(Agent), "a");
    var property = Expression.Property(parameter, propertyName);
    // a => a.PropertyName is a unary expression
    var unaryExpression = Expression.Convert(property, typeof(object));
    // Create the lambda for the unary expression with the given property 
    // information and compile to return the actual delegate.
    return Expression.Lambda<Func<Agent, object>>(unaryExpression, parameter).Compile();
}

Usage:

b.OrderBy(vm.SortByColumn) 

or

b.OrderByDescending(vm.SortByColumn) 

Hope this helps.

Parthasarathy
  • 2,698
  • 1
  • 12
  • 14
0

Try this:

var SortByColumnStr = "Answer"; //Dynamic string
var propertyInfo = typeof(Agent).GetProperty(SortByColumnStr);    
List<Agent> b.OrderBy(x => propertyInfo.GetValue(x, null));

Reference: https://stackoverflow.com/a/7265394/1660178

Community
  • 1
  • 1
sangram parmar
  • 8,462
  • 2
  • 23
  • 47
0

Take look at System.Linq.Dynamic (NuGet Package Available).

Then you can do just:

string sortingString = string.Format("{0} {1}",vm.OrderByColumn, vm.SortAscending ? "ASC" : "DESC");
vm.Agents = Agent.GetAgents().OrderBy(sortingString).ToList();
DonBaton
  • 43
  • 7