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