Sort Linq W tym wpisie opiszę program który:

 

Tworzy obiekt klasy “Person”, która reprezentuje osobę z rolą i imieniem.

  • Wyświetla kolekcje List<Person> w kontrolce GridView na stronie ASP.NET.
  • Sortuje kolekcje rosnąco i malejąco po jej właściwościach przy użyciu własnych metod bazujących na LINQ.

Z góry także mogę obiecać ,że rozbuduję ten program, ponieważ ostatnio w mojej pracy musiałem napisać bardziej zaawansowana wersję takiego sortowania. Chciałbym zachować zdobytą wiedzę w moich notatkach.

Na początek zacznijmy od klasy “Person” , która będzie reprezentować encje osoby. Posiada ona tylko dwa pola: nazwę i rolę. Nie ma tutaj żadnej rewelacji chcę uprościć ten przykład aż do bólu.

[Serializable]     
public class Person {
     public Person(string name,string role)         
     {   _name = name;             
         _role = role;         
     }         
    private string _name;         
    
    public string Name
    {
        get { return _name; }           
        set { _name = value ; }
    }         
    
    private string _role;          
    public string Role
    {
        get { return _role; }           
        set { _role = value ; }
    }         
    
    public override string ToString()
    {
        return string .Format("{0} {1}", _name, _role);
    }     
}

Do wyświetlenia kolekcji będzie potrzebna strona ASP.NET. Na stronie znajdują się dwa przyciski sortujące oraz kontrolka GridView. Skorzystałem z wbudowanego stylu klasycznego tej kontrolki.

<html>
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Button ID="Button1" runat="server" Text="Sortuj po imieniu" OnClick="Button1_Click" Width="150px" />
            <asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Sortuj po roli" Width="150px" />
        </div>
        <br />
        <asp:GridView ID="GridView1" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
            <AlternatingRowStyle BackColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <RowStyle BackColor="#EFF3FB" /> <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <SortedAscendingCellStyle BackColor="#F5F7FB" /> <SortedAscendingHeaderStyle BackColor="#6D95E1" />
            <SortedDescendingCellStyle BackColor="#E9EBEF" /> <SortedDescendingHeaderStyle BackColor="#4870BE" />
        </asp:GridView>
    </form>
</body>
</html>

A oto statyczna klasa, która posiada w sobie własnoręcznie napisane metody rozszerzeniowe w stylu LINQ.

Sortowanie listy

Zaletą tego zastosowania jest fakt ,że jeśli chcemy sortować nasze klasy w kolekcjach w tym wypadku jest to klasa “Person” musi ona implementować interfejsIComparable lub IComparable<T>. Jednak wtedy możemy sortować klasę tylko według jednej właściwości lub jednego mechanizmu. Co, jeśli chcemy sortować elastycznie po każdej właściwości klasy nawet tej, której jeszcze nie ma, ale w przyszłości być może będzie.

W sumie metoda rozszerzeniowa napisana poniżej ma napisaną całą mechanikę porównywania elementów. Korzysta z klasy“Compare” zupełnie tak, jak byśmy implementowali interfejs IComparable ,ale delegata zadziała dla każdej właściwości. Pod warunkiem ,że właściwość implementuje ten interfejs, co znaczy ,że na pewno ona zadziała dla wbudowanych typów .NET jak int czy string.

Jedynym ograniczeniem jest fakt ,że sortowana kolekcja elementów musi być kolekcją List<T>ponieważ w tym kodzie korzystam z wbudowanej metody Linq Sort tejże kolekcji.

public static class StaticSortingMethods {
    public static void Sort<TSource, TValue>(this List<TSource> source,
        Func<TSource, TValue> selector)
    {
        var comparer = Comparer<TValue>.Default; source.Sort(
            (x, y) => comparer.Compare(selector(x), selector(y)));
    } 
    public static void SortDescending<TSource, TValue>(this List<TSource> source, 
        Func<TSource, TValue> selector)
    {
        var comparer = Comparer<TValue>.Default; source.Sort(
            (x, y) => comparer.Compare(selector(y), selector(x)));
    }
}

Do problemu można podejść jeszcze inaczej. Można np. wykonać metodę LINQ zwaną OrderBy, która też może posortować kolekcje po jej właściwościach ,ale wtedy po takiej operacji trzeba byłoby jeszcze rzutować wynik na listę, za pomocą metodyToList(). A to jest efektowna operacja dla komputera.

List <Person > d2 = new List <Person>()
                         {
                             new Person ("Ala" ,"Administrator"),     
                             new Person ("Zosia" ,"Grafik"),     
                             new Person ("Kasia" ,"Moderator"),     
                             new Person ("Alicja" ,"Uźytkownik"),     
                             new Person ("Ula" ,"JuniorAdmin"),     
                             new Person ("Beata" ,"Wyróżniona"),     
                             new Person ("Dominika" ,"Administrator")
                         };  
var result = d2.OrderBy(item => item.Name); 
var result2 = from item in d2 orderby item.Role  select item;  
d2 = result.ToList(); 
List <Person> list = result2.ToList(); 
Jak widać rozwiązań i pomysłów jest wiele. Prawdopodobnie ich ilość można jeszcze zwiększyć, jeśli przyjrzymy się pewnym mechanizmom refleksji i wyrażeniom (Expressions) Linq.

W sumie to łącząc wyrażenia Linq i refleksje można byłoby stworzyć absolutnie elastyczną metodę sortującą. Zostawmy to jednak i zobaczmy jak działa to sortowanie w praktyce.
public partial class Test : System.Web.UI.Page {
    List <Person > d1 = new List <Person>()
    {
         new Person ("Ala" ,"Administrator"),
         new Person ("Zosia" ,"Grafik"),
         new Person ("Kasia" ,"Moderator"),
         new Person ("Alicja" ,"Użytkownik"),
         new Person ("Ula" ,"JuniorAdmin"),
         new Person ("Beata" ,"Wyróżniona"),
         new Person ("Dominika" ,"Administrator")
    };
 
     protected void Page_Load(object sender, EventArgs e)
     {
         if(!IsPostBack)
         {
             GridView1.DataSource = d1;
             Session["data"] = d1;
             GridView1.DataBind();
         }
     }
 
     protected void Button1_Click(object sender, EventArgs e)
     {
         StaticSortingMethods.Sort(d1, item => item.Name);
         GridView1.DataSource = d1;
         GridView1.DataBind();
     }
 
     protected void Button2_Click(object sender, EventArgs e)
     {
         StaticSortingMethods.Sort(d1, item => item.Role);
         GridView1.DataSource = d1;
         GridView1.DataBind();
     }
}

W powyższym kodzie tworzę sobie kolekcje Person ,a potem binduje ją do kontrolki GridView. Za pomocą przycisków możemy posortować kolekcje ,a potem zbindować ją ponownie do GridView.

Jak działa program

Tak wygląda tabelka przed jakimkolwiek sortowaniem.

Sortowanie Listy Linq_01

Tak wygląda tabelka po sortowaniu po imieniu.

Sortowanie Listy Linq_02

A tak wygląda tabelka po sortowaniu po roli.

Sortowanie Listy Linq_03

Ciąg dalszy nastąpi. W następnym wpisie rozbuduje tę siatkę, jak i samą metodę sortowania kolekcji.