Mvc’de Custom Paging, Sorting, Kolon Ekleme ve Excel’e Atma Bölüm2
Selamlar,
Bugün Bölüm1‘de WebGrid ile yaptığımız paging ve custom column konularına devam edeceğiz. Bu makalede dinamik custom sorting ve datayi Excel’e atma konularını inceleyeceğiz. Öncelikle WebGrid’deki kolonlara tıklanınca sorting nasıl yapılıyor onu inceleyelim.
Aşağıda görüldüğü gibi ‘body‘ altından WebGrid’deki kolonlara yani ‘#grid thead th‘ koşuluna uyan tüm kolon ismlerine erişilir. ‘click‘ durumunda önce Action’a gönderilecek data oluşturulur.
- sort: O anda sıralama işlemi yapılacak kolon.
- preSort: Bir önceki sıralama yapılan kolon. Bir önceki kolona göre tekrar sıralama yapılıp yapılmadığna bakılıp, sıralamanın yönü yani ‘sortdir‘ buna göre değiştirilir.
- sortdir: Sıralama yaapılacak yön. Yani ya yukarıdan aşağı (asc) ya da aşağıdan yukarı (desc). İlk hangi yöne doğru sıralama yapılmış ise mutlaka ters yöne çevrilir. Amaç aynı kolon için üstüste 2 kere sıralama yapılır ise önce ‘asc‘ sonra ‘desc‘ şeklinde çalışması sağlanır.
İlgili data oluşturulduktan sonra ‘EfficientSorting()‘ action’ına gönderilir. Geri dönen Html deger, WebGrid temizlenip yerine html data olarak basılır. Ayrıca önceki makalede bahsedilen footer, manuel olarak oluşturulup yine WebGrid’in sonuna eklenir.
paging.js(Devam):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
var _sortdir = 'asc'; var _preSort = ''; //Tabloda kolonlar tiklaninca sorting islemi yapar $("body").on("click", "#grid thead th", function (e) { e.preventDefault(); if (_sortdir == "desc" && $(this).text() != _preSort) { _sortdir = "asc"; } var data = { sort: $(this).text(), sortdir: _sortdir, presort: _preSort }; _preSort = $(this).text(); _sortdir = _sortdir == 'asc' ? 'desc' : 'asc'; $.getJSON("/Home/EfficientSorting", data, function (html) { // add the data to the table $("#grid").remove(); $("body").append(html.Data); // footer tekrardan sona eklenilir. $('#grid thead').after(footer); }); }); |
Aşağıdaki script kodu ile tablonun başındaki sıralama yapılacak kolonların üzerine gelindiğin de mouse’un değişimi ve clicklenebilir olduğunun gösterimi sağlanmıştır.
1 2 3 4 5 6 7 8 |
//Tabloda kolonlarin ustune gelince mouse isareti cikartir. $("body").on({ mouseenter: function () { $(this).css('cursor', 'pointer'); }, mouseleave: function () { } }, '#grid thead th'); |
HomeController.cs (EfficientSorting()): Aşağıda görüldüğü gibi sıralama işlemi sırasında önce database’e bağlanılıp ilgili datalar farklı yollar ile çekilmiştir. Önceki yöntemleride yorumlanarak koyulmuştur ki, size bir fikir verebilsin.
- presort ve sort ile şu anda ve önceden sıralama yapılan kolonlara bakılmış ve aynı ise ‘desc‘ yani tersten, bir daha sıralama işlemi yapılmıştır.
EfficientSorting’de Çıkarılan Kısımlar( Denenen başka yöntemler):
- Yorumlanan ilk yöntemde reflection kullanılmıştır. ‘GetProperty()‘ ile ‘Customer‘ tablosunda string karşılığa denk gelen ilgili kolon çekilmiştir. En başta ‘dbContext.Customers.OrderBy()‘ methodu içinde ilgili ‘GetProperty()‘ methodunun kullanılmasına linq izin vermemektedir. Nedeni ilgili linq çalıştırılınca, oluşturulacak olan SqlQuery, ilgili methodu yani GetProperty()‘i tanımlayamamaktadır.
- 2. Yöntem de yukarıdaki maddeden dolayı önce datalar komple çekilmiş ‘data = dbContext.Customers.ToList()‘ daha sonra reflection ile gelen sorgu tipine göre ‘ascending‘ veya ‘descending‘ olarak reflection ile çekilen ‘propertyInfo‘ ile birlikte tüm data üzerinden sıralama yapılmıştır. Tabiki bu da daha sonra performans nedeni ile kaldırılmıştır. Başta ilk 25 kayıt çekilmek istenirken sıralama amaçlı tüm kaydı çekmek doğru değildir:) Bu yapılacak olsa, boşuna manuel sorting yazılmasına gerek yoktur . WebGrid, bunu bizim için zaten yapıyor :) Ayrıca belli bir koşula göre ‘asc ve desc’ göre kodu dallandırmak kod kalabalığından başka birşey değildir.
Şimdi gelelim gerçek çözüme:
- Bu işin en hızlı ve pratik çözümü olarak dinamik query, akla en yatkın yöntemlerden biridir. Tabi bunun farklı birçok çözümü olabilir. Örneğin burada entity framework yerine Dapper‘da kullanılabilirdi. Bu örnekde, ‘dbContext.Customers.SqlQuery()‘ methodu kullanılarak sıralanacak tablo ismi ‘sort’ string olarak eklenip, sorgulama tipi(asc, desc) olan ‘sortdir’da ilgili string query’e eklenip, istenen sayıda kayıt ‘rowPerPage‘ ile çekilir ve data‘ya atanır.
- İlgili data doldurulduktan sonra ‘WebGrid’ nesnesi serverside da belirlenen parametrelere göre oluşturulup(canPage: false, canSort: false, rowsPerPage: rowPerPage) ‘GetHtml()‘ methodu ile ilgili kolonlar ve sitiller tanımlanıp, dönülecek json nesneye atanır. Burada da ‘Siparisler‘ adlı önceki makalede bahsedilen custom column tanımlanır.
- Önemli Not: WebGrid oluşturulurken bazı kolonlara allies verilmiştir. Yani gelen kolon ismi, gerçekte tabloda olan kolon ismi artık değildir. Örnek: (grid.Column(“CompanyName”, “Company Name”)). Bu durumda ilgili kolona göre sort işlemi yapıldığında hata alınır. Bunun çözümü ya allies hiç verilmeyecek yada aşağıdaki örnekte olduğu gibi boşluk ” ” karakteri ile bir allies verilip daha sonra tüm boşluklar ve ara boşluklar buradaki gibi temizlenecektir. ‘sort.Trim().Replace(” “, “”)’
- Son olarak geri dönülecek json nesne, oluşturulan WebGrid Html’i ve toplam sayfalama sayısı ile birlikte geri dönülür.
HomeController.cs/EfficientSorting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public JsonResult EfficientSorting(string sort, string sortdir, string presort) { using (NorthWindContext dbContext = new NorthWindContext()) { List<Customer> data; sort = sort.Trim().Replace(" ", ""); presort = presort.Trim().Replace(" ", ""); if (sortdir == "desc" && presort != sort) { sortdir = "asc"; } //2.Yöntem //data = dbContext.Customers.ToList(); //Gerçek ve Son Çözüm data = dbContext.Customers.SqlQuery("select top " + rowPerPage + " * from Customers order by " + sort + " " + sortdir).ToList(); /* 1.Yöntem //var propertyInfo = typeof(Customer).GetProperty(sort); if (sortdir == "ascending") //data = dbContext.Customers.OrderBy(cus => propertyInfo.GetValue(cus, null)).Take(rowPerPage).ToList(); data = data.OrderBy(cus => propertyInfo.GetValue(cus, null)).Take(rowPerPage).ToList(); else //data = dbContext.Customers.OrderByDescending(cus => propertyInfo.GetValue(cus, null)).Take(rowPerPage).ToList(); data = data.OrderByDescending(cus => propertyInfo.GetValue(cus, null)).Take(rowPerPage).ToList(); */ var grid = new WebGrid(data, canPage: false, canSort: false, rowsPerPage: rowPerPage); var htmlString = grid.GetHtml(tableStyle: "webGrid", headerStyle: "webgrid-header", footerStyle: "webgrid-footer", alternatingRowStyle: "webgrid-alternating-row", selectedRowStyle: "webgrid-selected-row", rowStyle: "webgrid-row-style", htmlAttributes: new { id = "grid" }, columns: grid.Columns( grid.Column( header: "Siparisler", format: (item) => new HtmlString("<input type='button' id='btnDetail' value='Detay' onclick=\"getDetail('" + item.CustomerID + "')\" />")), //grid.Column("CustomerID", "ID"), grid.Column("CustomerID", "CustomerID"), grid.Column("CompanyName", "Company Name"), grid.Column("ContactName", "Contact Name"), grid.Column("Address"), grid.Column("City"), grid.Column("Country"), grid.Column("Phone")) ); // Eger bir satirdaki row count toplam satir sayisi ile bölümünde kalan yok ise yani tam sayi ise, direk pagecount alinir degil ise 1 arttirilir. var _count = dbContext.Customers.Count() % rowPerPage == 0 ? dbContext.Customers.Count() / rowPerPage : (dbContext.Customers.Count() / rowPerPage) + 1; return Json(new { Data = htmlString.ToHtmlString(), Count = _count }, JsonRequestBehavior.AllowGet); } } //Değişken olarak gelen string kolon ismine göre Customer sınıfına ait ilgili property nesnesini verir. Burada reflection kullanılmıştır. /*private static object GetPropertyValue(object obj, string property) { System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(property); return propertyInfo.GetValue(obj, null); }*/ |
Şimdi sıra geldi ilgili sort işlemine göre paging’i değiştirmeye. Eğer CustomerID’ye göre bir sort işlemi yapılır ise, paging de bu sort yapılan kolona göre dataları sıralamalıdır.
HomeController.cs/EfficientPaging: Aşağıda görüldüğü gibi Paging methodu gösterilecek sayfa olan ‘page‘ parametresi haricinde ‘sort, sortdir‘ şeklinde 2 parametre daha almaktadır. Makalenin başında hatırlayacağınız gibi ‘sort’ sıralama yapılan kolonu ‘sortdir’‘da sıralam yapılacak yönü(asc, desc) belirtmektedir. Öncelikle sıralama işlemi olup olmadığına bakılıp, eger sıralama var ise ‘SqlQuery‘ kullanılıp dinamik queryler yazılır. Böylece parametre olarak gelen sıralanacak kolon ‘sort‘ ile sıralama yönü de ‘sortdir‘ ile ilgili query’e eklenir. Ayrıca sayfalama işlemi için MsSql’deki ‘FETCH, NEXT‘ yapısı kullanılmıştır. Eğer herhangi bir sıralama işlemi yok ise sayfalama için o zaman da linq ile ‘skip, take‘ yapısı kullanılmıştır. Çünkü dinamik query yazmaya gerek yoktur.
- Ayrıca yorumlanan satırlarda, önce tüm data çekilmiş ve daha sonra çekilen bu tüm data üzerinde sadece ‘CustomID’ye göre sıralama ve sayfalama yapılmıştır.
- Diğer bir durum, ‘grid.Column(“CustomerID”, “ID”)’ şeklinde WebGrid2’ye verilen allies kaldırılmıştır. Nedeni sorting işleminde ilgili kolon ismine göre uygun bir kolon bulunamamasıdır.
- Her sayfada toplam 25 kayıt gösterilmektedir..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
int rowPerPage = 25; [HttpGet] public JsonResult EfficientPaging(int? page, string sort, string sortdir) { using (NorthWindContext dbContext = new NorthWindContext()) { sort = sort?.Trim().Replace(" ", ""); int skip = page.HasValue ? page.Value - 1 : 0; var data = new List<Customer>(); //var data = dbContext.Customers.OrderBy(o => o.CustomerID).Skip(skip * rowPerPage).Take(rowPerPage).ToList(); if (sort != null && sort.Trim() != "") data = dbContext.Customers.SqlQuery("select * from Customers order by " + sort + " " + sortdir + " OFFSET " + (skip * rowPerPage) + " ROWS FETCH NEXT " + rowPerPage + " ROWS ONLY").ToList(); else data = dbContext.Customers.OrderBy(o => o.CustomerID).Skip(skip * rowPerPage).Take(rowPerPage).ToList(); var grid = new WebGrid(data, canPage: false, canSort: false, rowsPerPage: rowPerPage); var htmlString = grid.GetHtml(tableStyle: "webGrid", headerStyle: "webgrid-header", footerStyle: "webgrid-footer", alternatingRowStyle: "webgrid-alternating-row", selectedRowStyle: "webgrid-selected-row", rowStyle: "webgrid-row-style", htmlAttributes: new { id = "grid" }, columns: grid.Columns( //grid.Column("CustomerID", "ID"), grid.Column( header: "Siparisler", format: (item) => new HtmlString("<input type='button' id='btnDetail' value='Detay' onclick=\"getDetail('" + item.CustomerID + "')\" />")), grid.Column("CustomerID", "CustomerID"), grid.Column("CompanyName", "Company Name"), grid.Column("ContactName", "Contact Name"), grid.Column("Address"), grid.Column("City"), grid.Column("Country"), grid.Column("Phone")) ); // Eger bir satirdaki row count toplam satir sayisi ile bolumunda tam sayi ise direk pagecount alinir degil ise yani kalan var ise 1 arttirilir. var _count = dbContext.Customers.Count() % rowPerPage == 0 ? dbContext.Customers.Count() / rowPerPage : (dbContext.Customers.Count() / rowPerPage) + 1; return Json(new { Data = htmlString.ToHtmlString(), Count = _count }, JsonRequestBehavior.AllowGet); } } |
Yukarıda görüldüğü gibi ilgili data paging ve sorting işlemlerinden sonra çekilmiş ve oluşturulan bir WebGrid’e atanmıştır. Daha sonrada ilgili WebGrid’in, Html’i ve toplam sayfa sayısı geri dönülmüştür.
Şimdir sıra geldi ilgili datayı Excle’e atmaya: Aşağıda görüldüğü gibi controller tarafında sadece ‘View‘ dönülmektedir.
HomeController.cs/GenerateExcel:
1 2 3 4 |
public ActionResult GenerateExcel() { return View(); } |
Index.cshtm’deki paging.js: Yukarıda görülen excel resmine tıklandığı zaman ilgili View’a ‘iframe‘ ile gidilmesi ve üzerine gelindiğinde mouse ikonun’un değişimi aşağıdaki function ile sağlanmıştır.
1 2 3 4 5 6 7 8 9 10 |
$(function () { $(function () { $('#ExcelDiv').on('hover', function () { $(this).css('cursor', 'pointer'); }) $('#ExcelDiv').on('click', function () { $('<iframe src="/Home/GenerateExcel"></iframe>').appendTo('body').hide(); }); }); }); |
Views/GenerateExcel: Size örnek olması amacı ile ‘WebMatrix.Data‘ kullanılarak view ortamında DB’ye bağlanıp dinamik Query yazılmış ve tüm data ‘db.Query()‘ komutu ile çekilmiştir. Ayrıca ‘data.First().Columns‘ ile gelen kaydın kolon isimleri alınmıştır. İstenir ise ilgili data sunucu tarafından da çekilip model olarak ilgili view’a gönderilebilirdi. Burada önemli nokta ‘Response.AddHeader(“Content-disposition”, “attachment; filename=report.xls”)‘ bilgisi ile sayfanın excel formatında olacağının tanımlanmasıdır. Ayrıca ilgili kolonlar gezilerek kolon isimleri excel’in başına bir tablo içerisinde yazılır. Daha sonra çekilen tüm data gezilerek tablonun içerisine basılır. Index.cshtml’de Excel’e at butonuna tıklanılması ile ilgili ‘GenerateExcel’ view’ına gidilir. Tüm data view ortamında çekilip, kolon isimleri ile bir html table’a basılır ve sayfa hiç açılmadan excel formatında ilgili client’ın makinasına indirilir.
Not: Aşşağıdaki örnekde de görüldüğü gibi Excel’e istenirse, webgrid’deki tüm kolonlar değil de, istenilen kolonlar yine istenilen isimlendirme ile atılabilirler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@using WebMatrix.Data; @{ Layout = null; var db = Database.Open("NORTHWND"); var sql = "SELECT CustomerID, CompanyName, ContactName, Address, City, Country, Phone FROM Customers"; var data = db.Query(sql); var columns = data.First().Columns; Response.AddHeader("Content-disposition", "attachment; filename=report.xls"); Response.ContentType = "application/octet-stream"; } <table style="border: 1px solid #a59f9f;"> <tr style="font-weight: bold"> @foreach (var column in columns) { <td style="border: 1px solid #a59f9f;">@column</td> } </tr> @foreach (var row in data) { <tr> @foreach (var column in columns) { <td style="border: 1px solid #a59f9f;">@row[column]</td> } </tr> } </table> |
Böylece Mvc bir web uygulamasında extra bir tool kullanmadan manuel paging, sorting, bize özel bir column ve excel’e çıktı veren bir örnek yazdık. Böylece hem performansı arttırdık hem de tüm kontrolü ele alarak, istenilen özellikleri rahatlıkla değiştire bildik.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hoşçakalın.
Source Code: https://github.com/borakasmer/WebGridCustomPageSort
Source:
Elini<e sağlık hocam yine çok güzel bir makale olmuş.
Tesekkurler Murad..