Advertisement
TylerOhlsen

Arbitrary Length OrderBy

Jul 18th, 2014
378
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.10 KB | None | 0 0
  1. public static class OrderByExtensions
  2. {
  3.     /// <summary>
  4.     ///     Sorts the elements of a sequence in according to a collection of keys.
  5.     /// </summary>
  6.     /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
  7.     /// <param name="source">A sequence of values to order.</param>
  8.     /// <param name="keysInfo">A collection of order by key information.</param>
  9.     /// <returns>An <see cref="IOrderedQueryable{T}"/> whose elements are sorted according to the keys information.</returns>
  10.     /// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c></exception>
  11.     /// <exception cref="ArgumentException">
  12.     ///     <paramref name="keysInfo"/> is <c>null</c> or empty
  13.     ///     -or-
  14.     ///     <paramref name="keysInfo"/> contains a key that is not found on type <typeparamref name="TSource"/>.
  15.     /// </exception>
  16.     public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, params OrderByKeyInfo[] keysInfo)
  17.     {
  18.         if (source == null)
  19.             throw new ArgumentNullException("source");
  20.  
  21.         if (keysInfo == null || keysInfo.Length == 0)
  22.             throw new ArgumentException("Key selector cannot be null or empty.", "source");
  23.  
  24.         // Good candidate to be cached
  25.         var keySelectorMapping = BuildKeySelectorMapping<TSource>();
  26.  
  27.         var unknownKeys = keysInfo.Where(prop => !keySelectorMapping.ContainsKey(prop.KeyName)).Select(prop => prop.KeyName);
  28.         if (unknownKeys.Any())
  29.             throw new ArgumentException(string.Format("Unable to find one or more keys by name.  Type: {0}, Keys: {1}",
  30.                 typeof(TSource), string.Join(", ", unknownKeys)), "keysInfo");
  31.  
  32.         var firstKeyInfo = keysInfo.First();
  33.         var keySelector = keySelectorMapping[firstKeyInfo.KeyName];
  34.         var orderedSource = firstKeyInfo.Direction == OrderByDirection.Ascending ?
  35.             source.OrderBy(keySelector) :
  36.             source.OrderByDescending(keySelector);
  37.  
  38.         foreach (var keyInfo in keysInfo)
  39.         {
  40.             // Skip the first because already sorted by the first
  41.             if (keyInfo == firstKeyInfo)
  42.                 continue;
  43.  
  44.             keySelector = keySelectorMapping[keyInfo.KeyName];
  45.             orderedSource = keyInfo.Direction == OrderByDirection.Ascending ?
  46.                 orderedSource.ThenBy(keySelector) :
  47.                 orderedSource.ThenByDescending(keySelector);
  48.         }
  49.  
  50.         return orderedSource;
  51.     }
  52.  
  53.     /// <summary>
  54.     ///     Sorts the elements of a sequence in according to a collection of keys.
  55.     /// </summary>
  56.     /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
  57.     /// <param name="source">A sequence of values to order.</param>
  58.     /// <param name="keysInfo">A collection of order by key information.</param>
  59.     /// <returns>An <see cref="IEnumerable{T}"/> whose elements are sorted according to the keys information.</returns>
  60.     /// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c></exception>
  61.     /// <exception cref="ArgumentException">
  62.     ///     <paramref name="keysInfo"/> is <c>null</c> or empty
  63.     ///     -or-
  64.     ///     <paramref name="keysInfo"/> contains a key that is not found on type <typeparamref name="TSource"/>.
  65.     /// </exception>
  66.     public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, params OrderByKeyInfo[] keysInfo)
  67.     {
  68.         return source.AsQueryable().OrderBy(keysInfo).ToList();
  69.     }
  70.  
  71.     /// <summary>
  72.     /// Builds a mapping from the key name to a function to extract a key from an element.
  73.     /// </summary>
  74.     /// <typeparam name="TSource">The type of the source element.</typeparam>
  75.     /// <returns>A mapping from a key name to a lambda key selector.</returns>
  76.     private static Dictionary<string, Expression<Func<TSource, object>>> BuildKeySelectorMapping<TSource>()
  77.     {
  78.         var sourceType = typeof(TSource);
  79.  
  80.         var properties = sourceType.GetProperties();
  81.  
  82.         var keySelectorMapping = new Dictionary<string, Expression<Func<TSource, object>>>();
  83.         foreach (var propertyInfo in properties)
  84.         {
  85.             var localScopePropertyInfo = propertyInfo;
  86.             var name = propertyInfo.Name;
  87.             Expression<Func<TSource, object>> keySelector = source => localScopePropertyInfo.GetValue(source);
  88.             keySelectorMapping.Add(name, keySelector);
  89.         }
  90.  
  91.         return keySelectorMapping;
  92.     }
  93. }
  94.  
  95. public class OrderByKeyInfo
  96. {
  97.     /// <summary>
  98.     /// Initializes a new instance of the <see cref="OrderByKeyInfo"/> class.
  99.     /// </summary>
  100.     /// <param name="keyName">Name of the key.</param>
  101.     /// <param name="direction">The direction to order by.</param>
  102.     public OrderByKeyInfo(string keyName, OrderByDirection direction)
  103.     {
  104.         KeyName = keyName;
  105.         Direction = direction;
  106.     }
  107.  
  108.     public string KeyName { get; private set; }
  109.     public OrderByDirection Direction { get; private set; }
  110. }
  111.  
  112. public enum OrderByDirection
  113. {
  114.     Ascending,
  115.     Descending
  116. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement