Gelişmiş haber arama
Ben haber dedim ama siz istediğiniz gibi uyarlayabilirsiniz. :)
Sitelerimizde, arama işlemlerini sıklıkla kullanırız. LINQ + EF (LINQ + Entity Framework) ile çalışan arkadaşlar bu noktada sıkıntıya düşerler. Bu da son derece normaldir.
LINQ+EF kullanan arkadaşlara tavsiyem şudur: Bilgi ekleme/silme/listeleme işlemleri için LINQ+EF kullanabilirsiniz. Ama arama kısmı için enerjinizi boşu boşuna LINQ+EF ile harcamayın. Eski klasik yönteme geri dönün.
Yani klasik sql cümlecikleri ile bu işi halletmeye çalışın. (http://dalt.in/Fi6q3 linkindeki HelperDB ile ilgili yazım ilginizi çekebilir.)
Kod örneğini sizlerle paylaşmadan önce şunları belirtmeliyim:
- Katmanlı mimari ile hazırlandı.
- Sayfalama fonksiyonu eklendi. (nasıl olduğu ile ilgili detaylı açıklamayı daha sonra ayrı bir yazıda yapmaya çalışacağım.)
- Gerekli yerlerde class kullanımından kaçınılmadı.
- Yazının başında haber dediğim için veritabanı ve kodlar "haberler" ile ilgili.
- Veritabanı olarak MySQL kullanıldı. (Scriptler ve örnek datalar için aşağıdaki indirme linkini kullanabilirsiniz.)
- Örnek haber metinlerine takılmayın. Rastgele veriler olarak kabul edin.
- Önemli sayfaların kodlarını aşağıda bulabilirsiniz. Geriye kalan tüm dosyalar için sayfanın en altındaki indirme linkini kullanabilirsiniz.
- GenelIslemler.cs dosyasını daha önceden sizlerle paylaşmıştım.
- İlla LINQ+EF kullanmak istiyorsanız http://dalt.in/linqkit ve http://dalt.in/g4SGc linklerine göz atabilirsiniz. Ama emin olun ki direkt sql kodlarıyla çalışarak her zaman daha doğru ve hızlı sonuç alırsınız. Ayrıca çok karmaşık aramaları da ne yaparsanız yapın beceremezsiniz. :)
-
Kodlar gözünüze kalabalık / anlaşılmaz gelebilir ilk bakışta.
Ancak biraz incelerseniz aslında basit bir mantığı olduğunu sadece yazım işleminin biraz meşakkatli olduğunu farkedeceksiniz. - Arama parametrelerinde mümkün olduğunca çok veri tipi kullanılmaya çalışıldı. (string, int, datetime ve bool)
- Katmanlı mimari, sayfalama gibi yazı başlığı ile direkt ilgisi olmayan konulardan bahsedilmedi.
- Arama parametreleri QueryString'te tutukuyor. HaberAramaInfo sınıfı coding ve encoding işlerini hallediyor.
- Tasarım incelikleri göz ardı edildi.
- Listeleme detaylandırılmadı.
Şimdi ilk olarak proje yapısını göstereyim:
Kısaca;
- DAL - Data Access Layer => Veritabanına erişim için kullanılan class lar bu katmanda.
- HelperDB => Direkt olarak DB ye erişen ve sql kodlarını çalıştıran katman. (DAL'a gömülü)
- Info => Veritabanındaki tabloları ve programımızda gerekli olan veri yapılarının modellendiği katman.
- Web => Sitemiz :)
Gelelim kodları paylaşmaya...
Web.Config - ConnectionString
<connectionStrings> <add name="HaberConnection" connectionString="server=localhost;User Id=root;password=xxx;database=haberdb;charset='latin5';" providerName="MySql.Data.MySqlClient"/> </connectionStrings>
HelperDB sınıfı, ConnectionString bilgisini web.config ten okumaktadır.
Şimdi de modellemeler...
HaberInfo.cs
namespace DevrimAltinkurt.Info { public class HaberInfo { public int Id { get; set; } public string Baslik { get; set; } public string Ozet { get; set; } public string Ayrinti { get; set; } public DateTime Tarih { get; set; } public int Okunma { get; set; } public int IlId { get; set; } public bool Flash { get; set; } } }
HaberAramaInfo.cs
namespace DevrimAltinkurt.Info { public class HaberAramaInfo { public string AramaMetni { get; set; } public DateTime Tarih1 { get; set; } public DateTime Tarih2 { get; set; } public int IlId { get; set; } public string _Il { get; set; } public int Flash { get; set; } public HaberAramaInfo() { Tarih1 = DateTime.MinValue; Tarih2 = DateTime.MaxValue; } public string ToQueryString() { string s = ""; if (!string.IsNullOrEmpty(this.AramaMetni)) s += "|aramametni|" + this.AramaMetni; if (Tarih1 > DateTime.MinValue) s += "|tarih1|" + this.Tarih1.ToShortDateString().Replace("/", "-"); if (Tarih2 < DateTime.MaxValue) s += "|tarih2|" + this.Tarih2.ToShortDateString().Replace("/", "-"); if (this.IlId > 0) s += "|ilId|" + this.IlId; if (!string.IsNullOrEmpty(this._Il)) s += "|il|" + this._Il; if (this.Flash > 0) s += "|flash|" + this.Flash; return s; } public void FromQueryString(string s) { List<String> s2 = new List<string>(s.Split('|')); int sira = s2.IndexOf("aramametni"); if (sira > -1 && sira % 2 == 1) this.AramaMetni = s2[sira + 1]; sira = s2.IndexOf("tarih1"); if (sira > -1 && sira % 2 == 1) this.Tarih1 = Convert.ToDateTime(s2[sira + 1].Replace("-", "/")); sira = s2.IndexOf("tarih2"); if (sira > -1 && sira % 2 == 1) this.Tarih2 = Convert.ToDateTime(s2[sira + 1].Replace("-", "/")); sira = s2.IndexOf("flash"); if (sira > -1 && sira % 2 == 1) this.Flash = Convert.ToInt32(s2[sira + 1]); sira = s2.IndexOf("ilId"); if (sira > -1 && sira % 2 == 1) this.IlId = Convert.ToInt32(s2[sira + 1]); } } }
HaberDB
namespace DevrimAltinkurt.DAL { public class HaberDB { HelperDB helperDB = new HelperDB(); public List<HaberInfo> TumHaberleriGetir(int baslangic, int adet) { List<HaberInfo> haberler = new List<HaberInfo>(); string sql = "select * from haberler order by Id desc limit ?Baslangic, ?Adet"; var pars = new MySqlParameter[]{ new MySqlParameter("?Baslangic", MySqlDbType.Int32), new MySqlParameter("?Adet" , MySqlDbType.Int32) }; pars[0].Value = baslangic; pars[1].Value = adet; MySqlDataReader dr = helperDB.ExecuteReader(sql, pars); while (dr.Read()) { haberler.Add(new HaberInfo { Ayrinti = Genel.GetString(dr, "Ayrinti"), Baslik = Genel.GetString(dr, "Baslik"), Flash = Genel.GetBoolean(dr, "Flash"), Id = Genel.GetInteger(dr, "Id"), IlId = Genel.GetInteger(dr, "IlId"), Okunma = Genel.GetInteger(dr, "Okunma"), Ozet = Genel.GetString(dr, "Ozet"), Tarih = Genel.GetDateTime(dr, "Tarih") }); } dr.Close(); return haberler; } } }
Gelelim asp.net sayfalarımıza...
Arama Paneli
<asp:Panel ID="pnlArama" runat="server" DefaultButton="btnAra"> <b>Haber Arama</b> <hr /> Arama metni <br /> <asp:TextBox ID="txtAramaMetni" runat="server" /> <hr /> Kayıt Tarihi <br /> <asp:TextBox ID="txtTarih1" runat="server" Width="70" /> » <asp:TextBox ID="txtTarih2" runat="server" Width="70" /> <hr /> İlgili il <br /> <asp:DropDownList ID="ddlIller" runat="server" /> <hr /> Flash Haber <br /> <asp:RadioButtonList ID="rblFlash" runat="server" RepeatDirection="Horizontal" RepeatLayout="Flow"> <asp:ListItem Text="Tümü" Value="0" Selected="True" /> <asp:ListItem Text="Olanlar" Value="1" /> <asp:ListItem Text="Olmayanlar" Value="2" /> </asp:RadioButtonList> <hr /> <asp:Button ID="btnAra" runat="server" Text="Ara" OnClick="btnAra_Click" /> | <asp:LinkButton ID="lbTumunuGoster" runat="server" Text="Tümünü göster" OnClick="lbTumunuGoster_Click" /> </asp:Panel>
Haber Listeleme
<asp:GridView ID="gvHaberler" runat="server" AutoGenerateColumns="false"> <Columns> <asp:BoundField DataField="Id" HeaderText="Id" /> <asp:BoundField DataField="Baslik" HeaderText="Başlık" /> <asp:BoundField DataField="Tarih" HeaderText="Tarih" DataFormatString="{0:dd.MM.yyyy}" /> <asp:BoundField DataField="Il" HeaderText="İl" /> <asp:CheckBoxField DataField="Flash" HeaderText="Flash" /> </Columns> <EmptyDataTemplate> Haber bulunamadı! </EmptyDataTemplate> </asp:GridView>
Arama butonu
protected void btnAra_Click(object sender, EventArgs e) { hai = new HaberAramaInfo(); hai.AramaMetni = txtAramaMetni.Text.ToTemizMetin(); hai.Flash = rblFlash.SelectedValue.ToInt32(); hai.IlId = ddlIller.SelectedValue.ToInt32(); hai.Tarih1 = txtTarih1.Text.ToTemizMetin().isDate() ? txtTarih1.Text.ToDateTime() : DateTime.MinValue; hai.Tarih2 = txtTarih2.Text.ToTemizMetin().isDate() ? txtTarih2.Text.ToDateTime() : DateTime.MaxValue; string url = "/Default.aspx?Ara=" + hai.ToQueryString(); Response.Redirect(url, true); }
Aramayı gerçekleştiren kod
HaberAramaInfo hai = null; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ddlIller.DataSource = new IlDB().IlleriGetir(); ddlIller.DataTextField = "Il"; ddlIller.DataValueField = "Id"; ddlIller.DataBind(); ddlIller.Items.Insert(0, new ListItem("Seçiniz", "0")); string ara = ""; if (Request.QueryString["Ara"] != null) { ara = Server.UrlDecode(Request.QueryString["Ara"].ToString()); } HaberleriGetir(ara); } } private void HaberleriGetir(string aramaBilgisi) { HaberAramaInfo hai = new HaberAramaInfo(); hai.FromQueryString(aramaBilgisi); string sql_liste = @"select h.*, i.Il from haberler h inner join iller i on h.IlId = i.Id where 1=1"; string sql_adet = "select Count(Id) from haberler where 1=1"; string sql = " "; #region parametreler List<MySqlParameter> pars = new List<MySqlParameter>(); if (!string.IsNullOrEmpty(hai.AramaMetni)) { sql += " and (" + "Baslik like ?AramaMetni "; sql += " or Ayrinti like ?AramaMetni "; sql += " or Ozet like ?AramaMetni" + ") "; pars.Add((new MySqlParameter("?AramaMetni", MySqlDbType.String))); pars[pars.Count - 1].Value = "%" + hai.AramaMetni.ToTemizMetin() + "%"; txtAramaMetni.Text = hai.AramaMetni.ToTemizMetin(); } if (hai.IlId > 0) { sql += " and IlId = ?IlId"; pars.Add((new MySqlParameter("?IlId", MySqlDbType.Int32))); pars[pars.Count - 1].Value = ddlIller.SelectedValue = hai.IlId.ToString(); } if (hai.Flash > 0) { rblFlash.SelectedValue = hai.Flash.ToString(); sql += " and Flash = ?Flash"; pars.Add((new MySqlParameter("?Flash", MySqlDbType.Bit))); pars[pars.Count - 1].Value = hai.Flash == 1 ? true : false; rblFlash.SelectedValue = hai.Flash.ToString(); } if (hai.Tarih1 > DateTime.MinValue) { sql += " and Tarih > ?Tarih1"; pars.Add((new MySqlParameter("?Tarih1", MySqlDbType.Date))); pars[pars.Count - 1].Value = hai.Tarih1; txtTarih1.Text = hai.Tarih1.ToShortDateString(); } if (hai.Tarih2 < DateTime.MaxValue) { sql += " and Tarih < ?Tarih2"; pars.Add((new MySqlParameter("?Tarih2", MySqlDbType.Date))); pars[pars.Count - 1].Value = hai.Tarih2; txtTarih2.Text = hai.Tarih2.ToShortDateString(); } #endregion sql += " order by Tarih desc "; HelperDB helperDB = new HelperDB(); int toplam = helperDB.ExecuteScalar(sql_adet + sql, pars.ToArray()).ToInt32(); UC_Sayfalama1.Toplam = toplam; UC_Sayfalama1.Adet = 5; //if (toplam == 0) //{ //} //else //{ UC_Sayfalama1.Sayfala(); string sql_tum = sql_liste + sql + string.Format(" limit {0}, {1}", (UC_Sayfalama1.Sayfa - 1) * UC_Sayfalama1.Adet, UC_Sayfalama1.Adet); gvHaberler.DataSource = helperDB.ExecuteDataTable(sql_tum, pars.ToArray()); gvHaberler.DataBind(); //} }
Tabii ki using DevrimAltinkurt.DAL ve using DevrimAltinkurt.Info ifadelerini unutmuyorsunuz. Siz kendi istediğiniz namespaceleri kullanabilirsiniz.
Kodlar uzun ama mantık güzel, değil mi?
Şimdi gelelim bu sistemin avantajlarına:
- Bu sistemi bir kere kullandıktan sonra diğer arama sayfalarını çok hızlı gerçekleştirirsiniz.
- Arama parametreleri URL'de (QueryString) tutulduğu için istediğiniz forum sayfasına yazabilir, arkadaşlarınıza gönderebilir, facebook/twitter hesaplarınıza ekleyebilirsiniz..
- URL kısaltma servislerinden faydalanabilirsiniz.
- Arama butonunda oluşan URL bilgisini bir tabloda tutar, başka bir sayfada da bunları listelerseniz arama motorları açısından güzel bir iş yapmış olursunuz.
- DateTime, bool, integer, string bilgilerle çalışırken veritipine göre özel önlemler aldığımıızın farkındasınız değil mi? Örneğin tarih bilgisini kullanırken, tarih1 e Minimum, tarih2 ye Maksimum tarih değerlerini atadık. Farklı algoritmalar geliştirmede yardımcı olacağı kesin!!! :)
Dezavantajları:
- Kodları algılama ilk başta biraz zor olabilir. Ama gelişmiş arama yapmak isteyenler temel seviyenin üstündeki programcılardır diye mantık yürütebilirim.
- Kodlar sadece MySQL üzerinde çalışır.
- Parametre tip ve sayıları arttıkça kodlar da uzayacaktır.
Şimdi biraz da ekran görüntüsü paylaşayım:
Lütfen URL i inceleyiniz:
http://localhost:52382/Default.aspx?Ara=|aramametni|m%c3%bcjde|tarih1|20.12.2012|tarih2|01.01.2013|ilId|20|flash|2
Bütün proje kodlarını buradan indirebilirsiniz.
Herkese kolay gelsin.
Not: İleride belki bu uygulamanın LINQ+EF versiyonunu da hazırlayabilirim.
#gelişmiş #haber #arama #katmanlı-mimari #EF #linq