2012-05-07 5 views
5

Sto provando a creare una classe rapida in modo che possa rendere la scrittura del codice di ordinamento per una griglia molto più facile da gestire e mantenere e mantenere la ripetizione del codice verso il basso. Per fare questo mi si avvicinò con la seguente classe:Come è possibile memorizzare dinamicamente le espressioni utilizzate per 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; } 
    } 
} 

L'idea è di creare una configurazione rapida delle diverse opzioni di ordinamento, quello orderBy espressione è usata per questo, e opzionalmente un oggetto che è legata a quella opzione di ordinamento In questo modo il mio codice a guardare come:

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); 
    } 

e allora posso ordinare da solo facendo

 // 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); 

Questa grande opera quando la variabile ordinata è una stringa, ma quando non è una stringa ottengo la seguente eccezione: Cannot order by type 'System.Object'.

Suppongo che ciò sia dovuto al fatto che sto memorizzando l'espressione come Expression<Func<TSource, object>> e non essendo più specifico su quel 2 ° generico. Non capisco come posso mantenere tutte le mie diverse opzioni di ordinamento (anche per le proprietà non stringa) in una classe.

Credo che uno dei problemi è che la clausola Linq.OrderBy() prende Expression<Func<TSource, TKey>> come è il parametro, ma io non sono il confezionamento mia testa intorno a come Linq.OrderBy() è in grado di dedurre cosa TKey dovrebbe essere, e quindi non riesco a capire come approfittare di quella deduzione per memorizzare queste espressioni con l'appropriato TKey.

Qualche idea?

+0

Date un'occhiata a [questo] (http://weblogs.asp.net/scottgu/archive/2008/01 /07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx) – Nestor

+0

Grazie, ma preferirei che l'ordinamento sia specificato dal codice piuttosto che dalla stringa specificata, soprattutto dal momento che la struttura dei dati del mio la griglia non è un modello di dati 1: 1 con il mio database, quindi l'ordinamento deve essere tradotto dalla griglia specificata in un ordinamento db in ogni caso – KallDrexx

+0

Non stai usando una stringa magica quando ottieni comunque l'opzione di ordinamento? –

risposta

5

L'argomento generico è dedotta in questo modo:

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

Quando hai un IEnumerable<T>, il compilatore è in grado di dedurre che il TSource è T in questa situazione a causa della dichiarazione di metodo di estensione; quindi il metodo di estensione è già stato aggiunto per sapere che cos'è TSource. Per esempio:

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

Dal momento che si parte con un IEnumerable<int>, il compilatore può dedurre che l'espressione si aspetta è Func<int, TKey>, perché l'estensione sta interessando l'IEnumerable<int>. Quindi, poiché l'espressione restituisce un valore, il compilatore può dedurre il tipo rimanente, in questa situazione int, quindi diventa un Func<int, int> con questo esempio.

Ora, per quanto riguarda il problema specifico, è possibile impostare facilmente l'espressione per lavorare in modo appropriato se si specifica l'oggetto SortConfig in modo appropriato. Sembra che il tuo SortConfig abbia un delegato al momento. Se si specifica il proprio SortConfig per utilizzare un altro tipo, si acquisisce specificità. Esempio:

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

Il prossimo problema qui è come memorizzare i metodi di supporto in qualche formato. E la tua dichiarazione di classe si presenta in questo modo:

public class SortConfig<TSource> 

Allora tutti i tipi di dati dovrebbero allinearsi quando si richiama l'estensione OrderBy.

EDIT: Ecco un esempio di lavoro di quello che penso che si vuole fare:

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

In qualche modo riesco a capire dove stai andando, ma qual è il metodo 'void'? Add'? Intendi '_sortConfig.Add'? –

+0

Sì, in realtà non sapevo qual è la tua firma del metodo, quindi ho dovuto presupporre. – Tejs

+0

Questo ha senso, tranne che non sono sicuro di quale tipo dare la proprietà 'SortOption.SortExpression' per archiviare le mie espressioni – KallDrexx

Problemi correlati