Entity Framework’ün uygulama geliştirme hızını ne denli arttırdığını tekrar tekrar duymaktan sıkılmışsınızdır belki de. Bilhassa ASP.NET projelerinde bir veri kontrolünü UI tarafında EntityDataSource kullanarak projedeki entity modeline bağlayabilmekte ve çok hızlı şekilde sayfalar oluşturabilmekteyiz. EntityDataSource kullandığımız veri kontrollerinde sayfalama(paging) ve sıralama(sorting) işlemlerini de kolaylıkla yapabiliyoruz. Ancak EntityDataSource kullanmak projenin UI bağımlığını arttırmakta ve ilerleyen zamanlarda güncellemeler yapmak için UI içerisine girilmesini zorunlu kılmaktadır. Yine veri odaklı işlemleri UI tarafında yönetmenin çok doğru olmadığını hepimiz biliyoruz.
Katmanlı mimari ile geliştirdiğimiz bir projede veriye erişimi genellikle Data Access Layer’da gerçekleştirmekteyiz. Entity Framework kullandığımız projelerde de LINQ sorgularımızın bulunacağı metotların yine bu katmanda olmasını beklemekteyiz. Dolayısıyla bir ASP.NET projesinde kullanacağımız veri kontrollerinde sayfalama ve sıralama gibi işlemleri de bu katmanda yer alan metotlarda gerçekleştirmemiz gerekecektir. Bu yazıda bir veri kontrolünü DAL’da yer alan metotlarla en etkili şekilde nasıl besleyebileceğimizi, sıralama ve sayfalama işlemlerini nasıl gerçekleştirebileceğimizi anlatmaya çalışacağım.
İlk olarak Entity Framework’te sayfalama ve sıralama işlemlerini yaptığımız metotlara kısaca değinelim.
LINQ sorgularında kullanabileceğimiz orderby ifadesi veya OrderBy, OrderBy<T> ile OrderByDescending<T> metotları ile gerçekleştirebilmekteyiz. Bu metotlardan generic olanları lambda ifadelerini kullanabilirken, generic olmayan OrderBy metodu ise string olarak ifadeleri kabul edebilmektedir. Örneğin Urun isimli bir entity’de Fiyat adında bir property varsa “it.Fiyat DESC” şeklinde bir ifade ile elimizdeki listenin Fiyat’a göre tersten sıralanmasını sağlayabiliriz.
LINQ sorgularında sayfalama yapmak için başlangıç indeksi ve toplam kaç adet kaydın getirileceğini belirtmemiz gerekiyor. Başlangıç indeksine i, kayıt sayısına s isimlerini verirsek;
– i=0, s=10 denildiğinde sorgu sonucunun 0 ile 10 arasındaki kayıtlar
– i=10, s=10 denildiğinde sorgu sonucunun 10 ile 20 arasındaki kayıtlar
– i=20, s=10 denildiğinde sorgu sonucunun 20 ile 30 arasındaki kayıtlar
getirilecektir. i ve s değerinin sayfalama yapan bir veri kontrolünden geldiğini düşünürsek, s aslında pageSize değeri, i değeri ise bir önceki sayfadaki sonuncu kayıdın satır numarası anlamına gelir. LINQ sorgularında Skip ve Take metotları ile sorgu sonucu üzerinden belirli bir aralığı elde edebiliyoruz. Skip(20).Take(10) şeklindeki bir ifade ilk 20 kayıdı atla, sonrasındaki 10 kayıdı getir anlamını taşımaktadır.
NOT: Skip ve Take metotları kullanıldığında SQL Server’da çalıştırılan sorguda paging yapılacağı, yani row_number fonksiyonu kullanılacağı için verilerin hangi kolona göre sıralanacağı önceden bilinmelidir. Bu nedenle Skip ve Take’in kullanıldığı LINQ sorgularında mutlaka OrderBy ile sıralama yapılmalıdır.
LINQ sorgulamalarında kullanacağımız metotlara değindikten sonra DAL’da yazacağımız metotları oluşturalım. Sayfalama işleminin gerçekleşmesi için iki metoda ihtiyacımız olacak; ilkincisi ihtiyacımız olan verileri getirecek, ikincisi ise sayfalama yapmayan bir sorguda toplam kaç kayıdın geleceğini getirecek. İkinci metot sayesinde veri kontrolü pager kısmında kaç tane sayfa numarası çıkaracağını belirleyecek Yazacağımız metotları az sonra ObjectDataSouce ile birlikte kullanacağız. ObjectDataSource sayfalama esnasında veri kontrolünden aldığı değerleri çağıracağı business metoduna StartRowIndex ve MaximumRows isimli iki parametre gönderir. Sıralama ile ilgili parametreyi ise kodlama esnasında bizim belirtmemiz gerekmektedir. İşlerimizi kolaylaştırmak adına çağıracağımız metodun parametre isimlerini StartRowIndex ve MaximumRows olarak verelim. Aşağıda Urun isimli bir entity’den istediğimiz verileri döndüren DAL metotlarımız yer almaktadır.
Öncesinde Urun entity’mizi de görelim:
public class UrunHelper { public static List<Urun> GetUrun(int startRowIndex, int maximumRows, string orderBy) { using (TestEntities context = new TestEntities()) { List<Urun> result; if(string.IsNullOrEmpty(orderBy)) //Siralama verilmemisse KategoriId'ye gore sirala result = context.Urun.OrderBy(u => u.UrunId).Skip(startRowIndex).Take(maximumRows).ToList(); else result = context.Urun.OrderBy(orderBy).Skip(startRowIndex).Take(maximumRows).ToList(); return result; } } public static int GetUrunCount() { using (TestEntities context = new TestEntities()) { int count = context.Urun.Count(); return count; } } }
GetUrun metodu aldığı parametrelerle sayfalama ve sıralama kriterlerine göre uygun değerleri döndürmektedir. GetUrunCount metodu ise sayfalama ve sıralama yapılmaksızın sorgunun normal şartlarda toplam kaç kayıt döndüreceğini belirlemektedir. UI tarafında test amaçlı bir sayfaya GridView ekliyoruz ve grid kontrolünü bir ObjectDataSource’a bağlıyoruz. Kodlar aşağıda, açıklamaları kodun altındai paragrafta…
WebForm1.aspx
<asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True" AutoGenerateColumns="true" DataSourceID="odsUrun" PageSize="5" onsorting="GridView1_Sorting"> </asp:GridView> <asp:ObjectDataSource ID="odsUrun" runat="server" SelectMethod="GetUrun" SelectCountMethod="GetUrunCount" TypeName="PagingOrnek.DAL.UrunHelper" SortParameterName="orderBy" EnablePaging="True"> </asp:ObjectDataSource>
WebForm1.aspx.cs
public partial class WebForm1 : System.Web.UI.Page { protected void GridView1_Sorting(object sender, GridViewSortEventArgs e) { //Gridview'ın SortDirection ozelligi ile ilgili durum bilgisini duzgun saklayamamasi //nedeniyle burada sort direction bilgisini viewstate'de sakliyoruz. //Konu ile ilgili yazi icin bir onceki blog postumu inceleyebilirsiniz. string previousSortExpression = (string)ViewState["SortDirection"]; string sortExpression = e.SortExpression; SortDirection sortDirection = e.SortDirection; if (sortExpression.Equals(previousSortExpression)) { sortDirection = SortDirection.Descending; ViewState["SortDirection"] = string.Empty; } else ViewState["SortDirection"] = sortExpression; //LINQ sorgusuna ASC veya DESC ifadesi kabul ettiği için, uygun formatı elde ediyoruz string direction = sortDirection == SortDirection.Ascending ? "ASC" : "DESC"; e.SortExpression = string.Format("it.{0} {1}", e.SortExpression, direction); } }
ObjectDataSource kontrolü GetUrun ve GetUrunCount metotlarına bağlanmış durumda. Metot startRowIndex ve maximumRows parametrelerini Gridview’in o anki sayfa indeksi ve PageSize değerlerinden, orderBy değerini ise Sorting eventin’deki SortExpression değerinden almaktadır. LINQ sorgusu sıralama ifadesi olarak it.Fiyat DESC gibi bir ifade beklediği için biz de Sorting event metodunda bu özelliği uygun değere dönüştürüyoruz. Bu şekilde çalışan bir sayfada verilerin beşer beşer sayfalandığını, düzgün sıralamaların yapıldığını görebiliriz. Aşağıdaki ekran görüntüsü debug modda elde edilen değerleri göstermektedir.
Eğer grid üzerinde sadece sayfalama yapacaksanız ve sıralama işlemine ihtiyaç duymuyorsanız yukarıda yazdığım GetUrun metodunu kullanmanızda bir mahsur yok(orderBy parametresinin null kontrolü yapılmakta). Ancak metodu orderBy parametresi olmadan yazmak isterseniz ObhectDataSource’un SortParameterName parametresini çıkarmanız yeterli olacaktır.
Yukarıda en yakın haliyle bir sorguda sayfalama ve sıralama için DAL’da yer alan metotlarımızı nasıl yazabileceğimizi gördük. Peki getirilecek verilerde herhangi bir koşulumuz olsaydı metotta ve ObjectDataSource’da ne gibi değişiklikler yapmamız gerekiir? Yani çalışacak Select sorgumuzda bir Where koşulumuz olursa ne gibi değişiklikler yapmamız gerekecek?
Sayfaya bir DropDownList ekleyerek ürün kategorilerini getiriyoruz. Listeden seçilen kategoriye göre ürünlerin listelenmesini sağlıyoruz. Yani ilk başta Select * From Urun şeklinde olan sorgumuz Select * From Urun Where KategoriId=1 gibi bir hale dönüşecek. İlk olarak GetUrun metoduna kategoriId adında bir parametre eklememiz gerekecek. Bu değişiklik aynı şekilde GetUrunCount metodunda da yapılacak, zira artık tablodaki tüm
kayıtların sayısı değil, sadece istenilen kategorideki ürünlerin kayıt sayısına ihtiyacımız olacak. Metotların içerisindeki sorgularında Where metoduyla güncellenmesi gerekiyor. Aşağıda metotlarımızın son hali yer almaktadır.
public static List<Urun> GetUrun(int startRowIndex, int maximumRows, string orderBy, int kategoriId) { using (TestEntities context = new TestEntities()) { List<Urun> result; if (string.IsNullOrEmpty(orderBy)) result = context.Urun.OrderBy(u => u.UrunId).Where(u => u.KategoriId == kategoriId) .Skip(startRowIndex).Take(maximumRows).ToList(); else result = context.Urun.OrderBy(orderBy).Where(u => u.KategoriId == kategoriId) .Skip(startRowIndex).Take(maximumRows).ToList(); return result; } } public static int GetUrunCount(int kategoriId) { using (TestEntities context = new TestEntities()) { int count = context.Urun.Where(u => u.KategoriId == kategoriId).Count(); return count; } }
Metotları yeni parametremize göre düzenledik. Şimdi kategoriId değerinin metotlara nasıl aktarılacağına bakalım. DropDownList’ten seçilen değerin
ObjectDataSource aracılığıyla metotlara iletilebilmesi için odsUrun kontrolünün SelectParameters koleksiyonuna bu bilgiyi eklememiz gerekecektir. Değer bir server kontrolünden geleceği için kullanacağımız parametre ControlParameter olacaktır. Kodlarımız aşağıda:
<asp:DropDownList ID="ddlKategori" runat="server" DataSourceID="odsKategori" DataTextField="Ad" DataValueField="KategoriId" AutoPostBack="true"> </asp:DropDownList> <asp:ObjectDataSource ID="odsKategori" runat="server" SelectMethod="GetKategori" TypeName="PagingOrnek.DAL.KategoriHelper"></asp:ObjectDataSource> <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True" AutoGenerateColumns="true" DataSourceID="odsUrun" PageSize="5" OnSorting="GridView1_Sorting"> </asp:GridView> <asp:ObjectDataSource ID="odsUrun" runat="server" SelectMethod="GetUrun" SelectCountMethod="GetUrunCount" TypeName="PagingOrnek.DAL.UrunHelper" SortParameterName="orderBy" EnablePaging="True"> <SelectParameters> <asp:ControlParameter Name="kategoriId" ControlID="ddlKategori" />
</SelectParameters> </asp:ObjectDataSource>
Bu şekilde DropDownList’den gelen değeri DAL’da yazılmış metotlara ileterek kategoriId bilgisine göre filtreleme yapabiliyor ve filtrelenmiş veri üzerinde sayfalama ile sıralama işlemlerini düzgün şekilde gerçekleştirebildik. Buna benzer şekilde DAL’da yer alan metotlarınıza farklı parametreler ekleyip, ObjectDataSource’un SelectParameters koleksiyonunda da bu parametrelerin nereden nasıl geleceğini belirleyebilirsiniz.