2012-05-07 46 views
5

我正在尝试创建一个快速类,这样我就可以使网格的排序代码更容易编写和维护,并保持代码重复。要做到这一点,我想出了下面的类:如何动态存储用于Linq Orderby的表达式?

public class SortConfig<TSource, TRelatedObject> where TSource : class where TRelatedObject : class 
{ 
    public IList<SortOption> Options { get; protected set; } 
    public SortOption DefaultOption { get; set; } 

    public SortConfig() 
    { 
     Options = new List<SortOption>(); 
    } 

    public void Add(string name, Expression<Func<TSource, object>> sortExpression, TRelatedObject relatedObject, bool isDefault = false) 
    { 
     var option = new SortOption 
     { 
      FriendlyName = name, 
      SortExpression = sortExpression, 
      RelatedObject = relatedObject 
     }; 

     Options.Add(option); 

     if (isDefault) 
      DefaultOption = option; 
    } 

    public SortOption GetSortOption(string sortName) 
    { 
     if (sortName.EndsWith("asc", StringComparison.OrdinalIgnoreCase)) 
      sortName = sortName.Substring(0, sortName.LastIndexOf("asc", StringComparison.OrdinalIgnoreCase)); 
     else if (sortName.EndsWith("desc", StringComparison.OrdinalIgnoreCase)) 
      sortName = sortName.Substring(0, sortName.LastIndexOf("desc", StringComparison.OrdinalIgnoreCase)); 

     sortName = sortName.Trim(); 

     var option = Options.Where(x => x.FriendlyName.Trim().Equals(sortName, StringComparison.OrdinalIgnoreCase)) 
          .FirstOrDefault(); 
     if (option == null) 
     { 
      if (DefaultOption == null) 
       throw new InvalidOperationException(
        string.Format("No configuration found for sort type of '{0}', and no default sort configuration exists", sortName)); 

      option = DefaultOption; 
     } 

     return option; 
    } 

    public class SortOption 
    { 
     public string FriendlyName { get; set; } 
     public Expression<Func<TSource, object>> SortExpression { get; set; } 
     public TRelatedObject RelatedObject { get; set; } 
    } 
} 

的想法是,你创建的不同排序选项,什么ORDERBY表达的快速配置用于那些,和可选的是关系到一个对象排序选项。这使我的代码看起来像:

protected void InitSortConfig() 
    { 
     _sortConfig = new SortConfig<xosPodOptimizedSearch, HtmlAnchor>(); 
     _sortConfig.Add("name", (x => x.LastName), lnkSortName, true); 
     _sortConfig.Add("team", (x => x.SchoolName), lnkSortTeam); 
     _sortConfig.Add("rate", (x => x.XosRating), lnkSortRate); 
     _sortConfig.Add("pos", (x => x.ProjectedPositions), null); 
     _sortConfig.Add("height", (x => x.Height), lnkSortHeight); 
     _sortConfig.Add("weight", (x => x.Weight), lnkSortWeight); 
     _sortConfig.Add("city", (x => x.SchoolCity), lnkSortCity); 
     _sortConfig.Add("state", (x => x.SchoolState), lnkSortState); 
    } 

,然后我可以只是做

 // Get desired sorting configuration 
     InitSortConfig(); 
     var sortOption = _sortConfig.GetSortOption(sort); 
     bool isDescendingSort = sort.EndsWith("desc", StringComparison.OrdinalIgnoreCase); 

     // Setup columns 
     InitSortLinks(); 
     if (sortOption.RelatedObject != null) 
     { 
      // Make modifications to html anchor 
     } 

     // Form query 
     var query = PodDataContext.xosPodOptimizedSearches.AsQueryable(); 

     if (isDescendingSort) 
      query = query.OrderByDescending(sortOption.SortExpression); 
     else 
      query = query.OrderBy(sortOption.SortExpression); 

这个伟大的工程,当排序变量是一个字符串排序,但如果不是一个字符串,我得到以下例外:Cannot order by type 'System.Object'.

我假设这是因为我存储的表达式为Expression<Func<TSource, object>>而不是关于该第二个泛型更具体。我不明白我如何在一个类中保留所有不同的排序选项(即使是非字符串属性)。

我猜测的问题之一是,Linq.OrderBy()条款需要Expression<Func<TSource, TKey>>,因为它的参数,但我不换我周围Linq.OrderBy()如何能够推断TKey应该是什么样的头,所以我不明白如何利用优势推断用适当的TKey来存储这些表达式。

任何想法?

+0

看看[这里](http://weblogs.asp.net/scottgu/archive/2008/01 /07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx) – Nestor

+0

谢谢,但我宁愿排序代码指定,而不是字符串指定,特别是因为我的数据结构网格与我的数据库不是1:1数据模型,因此排序需要从网格指定的数据库转换为db排序 – KallDrexx

+0

不管您是否使用排序选项,是不是在使用魔术字符串? –

回答

5

通用的说法推断,像这样:

IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> enumerable, Func<TSource, TKey> expression) 

当你有一个IEnumerable<T>,编译器能够推断TSource是在这种情况下,因为扩展方法声明的T;所以扩展方法已经添加了知道什么TSource是。例如:

Enumerable.Range(0, 10).OrderBy(x => x) 

自从我们开始与IEnumerable<int>,编译器可以推断出,预计表达Func<int, TKey>,因为扩展影响IEnumerable<int>。接下来,因为你的表达式返回一个值,所以编译器可以推断出其余的类型,在这种情况下int,所以它变成了一个Func<int, int>这个例子。

现在,与您的特定问题密切相关,如果您适当地将您的SortConfig对象通用化,则可以轻松地设置表达式以适当地工作。看起来您的SortConfig现在需要Func<TSource, object>代表。如果您将您的SortConfig通用化为使用其他类型,则可以获得特定性。例如:

void Add<TSource, TKey>(string name, Func<TSource, TKey> expression) 

下一个问题在于如何以某种格式存储您的支持方法。而你的类声明如下所示:

public class SortConfig<TSource> 

然后所有的数据类型应该排队,当你调用OrderBy扩展。

编辑:这里是什么,我觉得你想要做一个工作示例:

static void Main(string[] args) 
    { 
     var list = Enumerable.Range(0, 10).Reverse().Select(x => new SampleClass { IntProperty = x, StringProperty = x + "String", DateTimeProperty = DateTime.Now.AddDays(x * -1) }); 

     SortContainer<SampleClass> container = new SortContainer<SampleClass>(); 
     container.Add("Int", x => x.IntProperty); 
     container.Add("String", x => x.StringProperty); 
     container.Add("DateTime", x => x.DateTimeProperty); 

     var sorter = container.GetSorterFor("Int"); 

     sorter.Sort(list).ForEach(x => Console.WriteLine(x.IntProperty)); 
     Console.ReadKey(); 
    } 

    public class SampleClass 
    { 
     public int IntProperty { get; set; } 
     public string StringProperty { get; set; } 
     public DateTime DateTimeProperty { get; set; } 
    } 

    public class SortContainer<TSource> 
    { 
     protected Dictionary<string, ISorter<TSource>> _sortTypes = new Dictionary<string, ISorter<TSource>>(); 

     public void Add<TKey>(string name, Func<TSource, TKey> sortExpression) 
     { 
      Sorter<TSource, TKey> sorter = new Sorter<TSource, TKey>(sortExpression); 
      _sortTypes.Add(name, sorter); 
     } 

     public ISorter<TSource> GetSorterFor(string name) 
     { 
      return _sortTypes[name]; 
     } 
    } 

    public class Sorter<TSource, TKey> : ISorter<TSource> 
    { 
     protected Func<TSource, TKey> _sortExpression = null; 

     public Sorter(Func<TSource, TKey> sortExpression) 
     { 
      _sortExpression = sortExpression; 
     } 

     public IOrderedEnumerable<TSource> Sort(IEnumerable<TSource> sourceEnumerable) 
     { 
      return sourceEnumerable.OrderBy(_sortExpression); 
     } 
    } 

    public interface ISorter<TSource> 
    { 
     IOrderedEnumerable<TSource> Sort(IEnumerable<TSource> sourceEnumerable); 
    } 
+0

我有点得到你要去的地方,但那是什么'void''Add'方法呢?你的意思是'_sortConfig.Add'? –

+0

是的,我实际上并不知道你的方法签名是什么,所以我不得不假设它。 – Tejs

+0

这是有道理的,除了我不知道什么类型的'SortOption.SortExpression'属性来存储我的表达式 – KallDrexx