https://buraksenyurt.com/Burak Selim Şenyurt - Asp.Net 2.02016-08-08T07:27:38+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://buraksenyurt.com/post/TFI-109-IIS-Uzerindeki-Uygulamalarc4b1-Kod-Yoluyla-OgrenmekTFİ 109 - IIS Üzerindeki Uygulamaları Kod Yoluyla Öğrenmek2014-08-27T13:52:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p>Diyelim ki sunucudaki <strong>IIS </strong>üzerinde konuşlandırdığınız Web uygulamalarının bir listesini almak istiyorsunuz. Bunun elbette pek çok yolu olduğunu biliyorsunuz. Bir Powershell script' i belki de işinizi görür. Ancak belki de siz bunu kendi geliştireceğiniz windows forms uygulamasında bu listeyi kullanmak istiyorsunuz. Ne yaparsınız? Kod yardımıyla <strong>IIS </strong>üzerindeki <strong>Application</strong>' ları, <strong>Site</strong>' ları öğrenebilir misiniz?</p>
<p>Aslında hep elinizin altında olan<em> (Windows\System32\inetsrv\Microsoft.Web.Administration.dll)</em> ve hatta isterseniz <strong>NuGet Package Manager </strong>ile de indirebileceğiniz <strong>Microsoft.Web.Administration</strong> kütüphanesini kullanarak bu işi gerçekleştirmeniz oldukça kolay. Nasıl mı? İşte böyle.</p>
<p><img src="http://www.buraksenyurt.com/pics/2014%2f8%2ftfi109.png" alt="" /></p>
<p>Başka neler mi yapabilirsiniz? Örneğin bir Application Pool' u Recylce edebilirsiniz. Ya da bir Web Site' ı Stop-Start. Hatta yeni bir Web Site bile açabilirsiniz. Araştırmaya değer değil mi?</p>
<p>Başka bir ipucunda görüşmek dileğiyle.</p>2014-08-27T13:52:00+00:00bsenyurthttps://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=b0a0f719-7be3-43f8-96a5-6635b286bc471https://buraksenyurt.com/trackback.axd?id=b0a0f719-7be3-43f8-96a5-6635b286bc47https://buraksenyurt.com/post/TFI-109-IIS-Uzerindeki-Uygulamalarc4b1-Kod-Yoluyla-Ogrenmek#commenthttps://buraksenyurt.com/syndication.axd?post=b0a0f719-7be3-43f8-96a5-6635b286bc47https://buraksenyurt.com/post/Nasc4b1l-Yapc4b1lc4b1r-Adc4b1m-Adc4b1m-Ozel-HttpHandler-bsenyurt-com-danNasıl Yapılır? Adım Adım Özel HttpHandler2007-12-10T12:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Uzun zaman önce <strong>Asp.Net 2.0</strong> ile ilişkili makalelerimizden birisinde <strong>HttpHandler</strong> ve <strong>HttpModule</strong> kavramlarından bahsetmeye çalışmıştık. Bu makalemizde kendi Handler sınıfımızı geliştirmek isteyebileceğimiz örnek bir senaryo üzerinde daha durmaya çalışacağız. Bu sayede HttpHandler sınıfları yazarak neler yapılabileceğinide daha net bir şekilde görmüş olacağız. Konuyu daha net kavrayabilmek adına örnek senaryomuz üzerinden adım adım ilerleyeceğiz.</p>
<p>Bilindiği üzere web sunucusuna istemci tarafından gelen <strong>talepler(Requests) </strong>bazı <strong>program ara yüzleri(API) </strong>tarafından karşılanır ve uygun ortamlara işletilmek üzere iletilirler. Özellikle<strong> Asp.Net</strong> ile geliştirilen web uygulamalarında, talep edilen dosya tipine göre devreye giren HttpHandler sınıfları bulunmaktadır. Söz gelimi <strong>aspx</strong> uzantılı dosyalar <strong>PageHandlerFactory</strong> isimli sınıf tarafından ele alınırlar. <strong>PageHandlerFactory</strong> ve benzer işlevselliklere sahip handler tipleri <strong>IHttpHandler arayüzünü(interface) </strong>uygularlar. Dolayısıyla geliştiriciler kendi Handler tiplerini <strong>IHttpHandler</strong> arayüzünü kullanarak yazabilirler.</p>
<p>Gelelim örnek senaryomuza. Sunucu üzerinde barındırılan <strong>XML(eXtensible Markup Language) </strong>tabanlı basit bir metin dosyasını ele alacak özel bir <strong>Handler</strong> tipi geliştiriyor olacağız. XML tabanlı dosya içerisinde yer alan bilgilerden yararlanılarak ekrana herhangibir sorguya ait raporlama sonuçları aktarılacak. Dosyanın uzantısının örnek olarak <strong>rapx</strong> olduğunu düşünebiliriz. Peki bu raporlama işleminde önemli bir rol oynacak olan XML içeriğinde neler olması gerekmektedir? Bunların tespiti <strong>XML</strong> dosyasının mantıksal ağaç yapısının oluşturulmasınıda kolaylaştıracaktır. Söz konusu ihtiyaçları aşağıdaki maddeler halinde sıralanabilirler.</p>
<ul>
<li>Hazırlanan rapor için bir <strong>başlık(Title) </strong>bilgisi tutulabilir. Hatta başlığın <strong>arka plan rengi(Background Color)</strong> verilerek zengin görünmesi sağlanabilir.</li>
<li>Rapor dosyasının kaç adet <strong>sorgu cümlesi(Query) </strong>için destek vereceğine karar vermek gerekmektedir. Örneğin basit olması açısından başlangıç itibariyle sadece tek bir sorgu sonucunun ele alınmasında fayda vardır.</li>
<li>Sorgu cümlesi, <strong>saklı yordam(stored procedure)</strong> veya text tabanlı ifadeleri işaret edebilir. Hatta bir veritabanı <strong> görünümünün(View) </strong>desteklenmesi bile sağlanabilir.</li>
<li>Sorgu cümlesinde parametre kullanımına destek verilebilir. Bu parametrelerinin <strong>QueryString</strong> yardımıyla Url üzerinden veya XML içeriğinden alınacağına dair tanımlamalar yapılabilir.</li>
<li>Sorgu cümlesinin çalışacağı <strong>sunucu(Server)</strong> ve <strong>veritabanı(Database) </strong>adı belirtilmelidir. Hatta SSPI ile bağlantıya destek verilmeside göz önüne alınmalıdır.</li>
<li>Hazırlanan raporların <strong>XML</strong> içeriğinde belirtilen mail adreslerine gönderilmesi de sağlanabilir.</li>
<li>Raporun kimler tarafında görülebileceğine dair tanımlamalar yapılabilir. Bu tanımlamalar <strong>kullanıcı(User)</strong> ve hatta <strong>rol(Role)</strong> bazında gerçekleştirilebilir.</li>
</ul>
<p>Bu ihtiyaçlar dahada arttırılabilir. Bir anlamda geliştiricinin hayal gücü burada önem kazanmaktadır. Yukarıdaki maddelerde sözü geçen ihtiyaçların tamamı <strong>XML</strong>' in temel kavramları ile karşılanabilir. Bir başka deyişle <strong>eleman(element)</strong> ve <strong>nitelikler(attributes)</strong> sayesinde yukarıdaki istekler standartlara uygun olacak şekilde tasarlanabilir.</p>
<table id="table180" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td valign="top" width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><em>Söz konusu XML içeriğinin <strong>çalışma zamanında(run time)</strong> uygun bir standartta olduğunun garanti altına alınması için <strong>şema (Xml Schema)</strong> kullanılması yararlı olacaktır. Bu şema ile Xml içeriğinin çarpıştırılması Handler tipi içerisinde yapılabilir. Şemaya uymayan XML içerikleri için <strong>Handler</strong>, istekleri özel olarak tasarlanmış bir hata sayfasına doğru yönlendirebilir.</em></td>
</tr>
</tbody>
</table>
<p><br />Yukarıdaki maddeler ışığında aşağıdaki gibi örnek bir <strong>XML</strong> içeriği göz önüne alınabilir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<Raporlar>
<Rapor Id="">
<Baslik ArkaPlan=""></Baslik>
<Baglanti SSPI="">
<Sunucu></Sunucu>
<Veritabani></Veritabani>
<KullaniciAdi></KullaniciAdi>
<Sifre></Sifre>
</Baglanti>
<Sorgu Sp="">
<Cumle></Cumle>
<Parametreler Nereden="">
<Parametre Ad="" Deger=""/>
</Parametreler>
</Sorgu>
<MailListesi MailGonder="">
<Mail></Mail>
<Mail></Mail>
</MailListesi>
</Rapor>
</Raporlar></pre>
<p>Raporlar <strong>ana boğumu(Root Node)</strong> birden fazla <strong>Rapor</strong> elementi içerebilir. Biz örneğin daha kolay ele alınabilmesi amacıyla tek bir Rapor elementi kullanıyor olacağız. Raporun alınacağı <strong>sunucu(Server), veritabanı adı(Database Name), kullanıcı adı(User name) </strong>ve <strong>şifre(Password)</strong> bilgileri ise Baglanti elementi altında tutulmaktadır. Hatta <strong>SSPI</strong> kullanımınada destek verilmesi amacıyla Baglanti elementi içerisinde bir <strong>nitelik(attribute)</strong> tanımlanmaktadır. Sorguların <strong>saklı yordam(Stored Procedure)</strong> olup olmadığı Sp niteliği ile belirtilebilir. Bunun dışında sorgu içerisinde kullanılan parametreler var ise bunların nereden alınacağı Nereden isimli <strong>nitelik(attribute)</strong> ile belirlenmektedir. Öyleki raporun parametreleri, rapx dosyasına tarayıcı üzerinden yapılan çağrılarda <strong>QueryString</strong> yardımıyla gelebilir. Yada Parametre elementleri içerisindeki Deger niteliklerinde doğrudan tanımlanabilir. Üretilen raporların mail yolu ile kimlere bildirileceğine dair MailListesi elementi ve Mail alt elementleri kullanılabilir.</p>
<p>Burada ortaya çıkan önemli bir ihtiyaç vardır. <strong>XML</strong> bilgilerinin tutulduğu rapx uzantılı dosyaların işlenmesi, bir <strong>HTML</strong> çıktısının üretilmesi ve talepte bulunan istemcilere gönderilmesi sağlanmalıdır. Bunu sadece özel bir Handler tipi karşılayabilir.</p>
<p>Dilerseniz vakit kaybetmeden <strong>Handler</strong> tipini yazarak işe başlayalım. Geliştirilecek olan Handler sınıfı ilk etapta tek bir web uygulamasında kullanılacaktır. Sonrasında ise bu handler tipinin sunucu üzerindeki her Asp.Net web uygulaması için geçerli olması sağlanacaktır. Bu nedenle <strong>Handler</strong> tipinin bir <strong>sınıf kütüphanesi(Class Library) </strong>içerisinde olmasında yarar vardır. Handler sınıfının <strong>IHttpHandler</strong> <strong>arayüzünden(Interface)</strong> türetilmesi içinde sınıf kütüphanesine <strong>System.Web.dll assembly</strong>' ının referans edilmesi şarttır. Söz konusu <strong>Handler</strong> sınıfının diagram görüntüsü ve içeriği aşağıdaki gibidir.</p>
<p><img src="/makale/images/mk234_8.gif" alt="" width="448" height="270" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Web;
using System.Data;
using System.Web.Hosting;
using System.Data.SqlClient;
using System.Xml;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.IO;
namespace ReportHandlerLibrary
{
public class RaporHandler:IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return false; }
}
// Gelen talep sonrası üretilecek HTML çıktısını bu metod içerisinden veriyor olacağız
public void ProcessRequest(HttpContext context)
{
// Talep edilen sayfa Request.Path ile yakalanır.
// VirtualPathProvider metodu ile sanal adresin karşılığı olan fiziki yoldaki dosya açılarak Stream halinde elde edilir.
// Stream'den yararlanılarak XML içeriği XmlDocument nesnesi içerisine alınır.
XmlDocument xDoc = new XmlDocument();
xDoc.Load(VirtualPathProvider.OpenFile(context.Request.Path));
if (context.Request.QueryString["MailGonder"] == null)
{
// Baslik elementinden raporun başlığı bilgisi alınır
string baslik = xDoc.SelectSingleNode("Raporlar/Rapor/Baslik").InnerText;
string baslikArkaPlanRengi = xDoc.SelectSingleNode("Raporlar/Rapor/Baslik").Attributes["ArkaPlan"].InnerText;
// Ekran tasarımı oluşturulmaya başlanır
// Üretilen çıktı bir HTML sayfası olacağından HTML elementlerinin kullanılması gerekmektedir.
context.Response.Write("<HTML><HEAD><TITLE>" + baslik + "</TITLE></HEAD>");
context.Response.Write("<BODY>");
// Table elementi oluşturulur.
context.Response.Write("<TABLE border='1' width='100%' cellspacing='0' cellpadding='5'>");
// Tablo 3 satırdan oluşmaktadır. İlk satırın arka plan rengi ve içerisinde yer alacak metin bilgisi Baslik elementi ve ArkaPlan niteliklerinden alınır
context.Response.Write("<TD style='background-color:" + baslikArkaPlanRengi + "'>");
context.Response.Write("<H3>" + baslik + "</H3>");
context.Response.Write("</TD></TR>");
// İkinci satır içerisinde rapor sonucu üretilen grid içeriği olmalıdır.
context.Response.Write("<TR><TD>");
// Bu hücreye veri ile doldurulan Grid içeriğinin HTML çıktısı yazdırılır
context.Response.Write(GridHTMLUret(xDoc,context).ToString());
context.Response.Write("</TD></TR>");
context.Response.Write("<TR><TD>");
// Mail gönderme aksiyonu için basit bir hyperLink elementi eklenir
context.Response.Write("<a href='"+context.Request.Path + "?MailGonder=1'>" + "Raporu Mail Olarak Gönder" + "</a>");
context.Response.Write("</TD></TR>");
context.Response.Write("</BODY>");
context.Response.Write("</HTML>");
}
else
{
bool mailGondersinmi = false;
Boolean.TryParse(xDoc.SelectSingleNode("Raporlar/Rapor/MailListesi").Attributes["MailGonder"].Value, out mailGondersinmi);
if (mailGondersinmi)
MailGonder(xDoc,context);
}
}
#endregion
// GridView kontrolünün içeriği doldurulduktan sonra HTML içeriği elde edilir ve bu içeriği taşıyan StringWriter geriye döndürülür.
private StringWriter GridHTMLUret(XmlDocument xDoc,HttpContext ctx)
{
// SqlDataAdapter nesne oluşturulur
SqlDataAdapter adapter = new SqlDataAdapter(KomutHazirla(xDoc,ctx));
DataTable table = new DataTable();
// DataTable doldurulur
adapter.Fill(table);
// GridView kontrolü üretilir ve veriye bağlanır
GridView grd = new GridView();
grd.DataSource = table;
grd.DataBind();
//GridView kontrolünün HTML çıktısı elde edilir
StringWriter strWriter = new StringWriter();
HtmlTextWriter writer = new HtmlTextWriter(strWriter);
grd.RenderControl(writer);
return strWriter;
}
// Raporun üretilmesi için gerekli SqlCommand hazırlanıyor.
private SqlCommand KomutHazirla(XmlDocument xDoc,HttpContext ctx)
{
bool sp = false;
// Sorgu cümlesinin Stored Procedure olup olmadığı belirlenir.
Boolean.TryParse(xDoc.SelectSingleNode("Raporlar/Rapor/Sorgu").Attributes["Sp"].Value, out sp);
// SqlCommand tipi hazırlanır
SqlCommand cmd = new SqlCommand();
// Sorgu cümlesi alınır
cmd.CommandText = xDoc.SelectSingleNode("Raporlar/Rapor/Sorgu/Cumle").InnerText.Trim();
// Bağlantı cümlesi BaglantiCumlesiOlustur metodundan elde edilir ve Command için gerekli SqlConnection hazırlanır.
cmd.Connection = new SqlConnection(BaglantiCumlesiOlustur(xDoc));
// Eğer cümle Stored Procedure adını işaret ediyorsa CommandType için StoredProcedure enum sabiti değeri verilir
if (sp)
cmd.CommandType = CommandType.StoredProcedure;
// Eğer girilmiş parametreler varsa bu parametreler Command nesnesine AddWithValue metodu ile teker teker eklenir.
if (xDoc.SelectSingleNode("Raporlar/Rapor/Sorgu/Parametreler").ChildNodes.Count > 0)
{
string parametreNereden = xDoc.SelectSingleNode("Raporlar/Rapor/Sorgu/Parametreler").Attributes["Nereden"].Value;
XmlNodeList parametreler = xDoc.SelectSingleNode("Raporlar/Rapor/Sorgu/Parametreler").ChildNodes;
foreach (XmlNode parametre in parametreler)
{
if(parametreNereden=="Xml")
cmd.Parameters.AddWithValue(parametre.Attributes["Ad"].Value, parametre.Attributes["Deger"].Value);
else if(parametreNereden=="QueryString")
cmd.Parameters.AddWithValue(parametre.Attributes["Ad"].Value, ctx.Request.QueryString[parametre.Attributes["Ad"].Value.Substring(1, parametre.Attributes["Ad"].Value.Length - 1)]);
}
}
// Oluşturulan Command nesnesi geri döndürülür.
return cmd;
}
// Sorguların çalıştırılması için gerekli Bağlantı cümlesini oluşturan metod
private string BaglantiCumlesiOlustur(XmlDocument xDoc)
{
bool sspi = false;
// Sql bağlantısı için gerekli bağlantı cümlesi(Connection String) SqlConnectionStringBuilder sınıfı yardımıyla oluşturulur.
SqlConnectionStringBuilder conStrBuilder = new SqlConnectionStringBuilder();
// Sunucu ve veritabanı bilgileri XPath ifadeleri ile alınır.
conStrBuilder.DataSource = xDoc.SelectSingleNode("Raporlar/Rapor/Baglanti/Sunucu").InnerText;
conStrBuilder.InitialCatalog = xDoc.SelectSingleNode("Raporlar/Rapor/Baglanti/Veritabani").InnerText;
Boolean.TryParse(xDoc.SelectSingleNode("Raporlar/Rapor/Baglanti").Attributes["Sspi"].Value, out sspi);
if (!sspi) // Eğer integrated security ile bağlanılmıyorsa kullanıcı adı(UserID) ve şifre(Password) bilgileri alınır.
{
conStrBuilder.UserID = xDoc.SelectSingleNode("Raporlar/Rapor/Baglanti/KullaniciAdi").InnerText;
conStrBuilder.Password = xDoc.SelectSingleNode("Raporlar/Rapor/Baglanti/Sifre").InnerText;
}
else
conStrBuilder.IntegratedSecurity = true;
// Oluşturulan bağlantı cümlesi(Connection String) geri döndürülür.
return conStrBuilder.ConnectionString;
}
// Mail gönderme seçeneği aktif ise postaların gönderilme işlemini gerçekleştirecek olan metod.
private void MailGonder(XmlDocument doc,HttpContext ctx)
{
// Mail listesi MailListesi elementinin alt elementlerinden çekilir.
XmlNodeList mailler = doc.SelectSingleNode("Raporlar/Rapor/MailListesi").ChildNodes;
foreach (XmlNode mail in mailler)
{
//TODO: Bu noktada raporun mail olarak gönderilmesine ait işlemler yapılacaktır.
ctx.Response.Write(mail.InnerText + " adresine rapor mail olarak gönderildi<br/>");
}
}
}
}</pre>
<p>Geliştirilen sınıf içerisinde parçaları daha kolay ele alabilmek adına yardımcı metodlar yer almaktadır. Dikkat edileceği üzere rapx içeriğinin <strong>XML</strong> formatında tasarlanması, işlemleri son derece kolaylaştırmaktadır. Nitekim içeriğin <strong>XmlDocument</strong> sınıfına ait bir nesne örneği ile belleğe alınması, içerisindeki elementlerin veya niteliklerin <strong>XPath</strong> ifadeleri ile yakalanması son derece kolaydır. Üstelik XML, platform bağımsızlık sunduğundan bu dökümanın başka bir ortama gönderilerek ele alınmasının sağlanması daha kolaydır.</p>
<p>Bu sebepten dolayı günümüzün popüler kavramlarından olan <strong>Reporting Services </strong>veya <strong>LINQ To SQL</strong> içerisinde yer alan <strong>Database Markup Language(dbml) </strong>gibi yapılar XML üzerine oturmaktadır. Sınıf içerisinde dikkat edilmesi gereken noktalardan bir diğeri ise, <strong>HttpContext</strong> tipinin ektin şekilde kullanımıdır. HttpContext üzerinden ele alınan üyeler ile, üretilecek <strong>HTML</strong> içeriğinin tasarlanması veya talep ile gelen QueryString' lerin yakalanması söz konusudur. Hatta talep edilen sayfanın <strong>sanal yolu(Virtual Path) </strong>elde edilip <strong>XML</strong> içeriğinin <strong>VirtualPathProvider</strong> sınıfının <strong>OpenFile</strong> metodu ile kolay bir şekilde <strong>Stream</strong>' e dönüştürülmeside sağlanmaktadır. İşlemleri kolaylaştıran noktalardan biriside web kontrollerinin <strong>RenderControl</strong> metodudur. Bu metod ile kompleks bir <strong>GridView</strong> kontrolünün veri dolu içeriğinin <strong>HTML</strong> çıktısını almak son derece kolaylaşmaktadır.</p>
<p>Şimdi geliştirilen <strong>Handler</strong> tipini örnek bir web uygulamasında test edebiliriz. İlk olarak tek bir web uygulamasına özel olacak şekilde Handler tipinin kullanılmasını ele alacağız. Böyle bir durumda öncelikli olarak web uygulamasının Handler tipini içeren <strong>sınıf kütüphanesini(Class Library)</strong> referans etmesi gerekmektedir. Örnek olarak OzelHandlerKullanimi isimli web uygulaması aşağıdaki ekran görüntüsünden de anlaşılacağı üzere ReportHandlerLibrary isimli <strong> assembly</strong>' ı referans etmektedir.</p>
<p><img src="/makale/images/mk234_1.gif" alt="" width="283" height="372" border="0" /></p>
<p>Bunun dışında web uygulamasına ait <strong>web.config </strong>dosyası içerisinde aşağıda görüldüğü gibi gerekli bildirimler yapılmalıdır.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<httpHandlers>
<add path="*.rapx" type="ReportHandlerLibrary.RaporHandler,ReportHandlerLibrary" verb="*" validate="true"/>
</httpHandlers>
<compilation debug="true"/>
<authentication mode="Windows"/>
</system.web>
</configuration></pre>
<p><strong>httpHandlers</strong> elementi <strong>system.web</strong> elementi içerisinde tanımlanmalıdır. Örnekte <strong>rapx</strong> uzantılı dosyalara gelecek herhangibir talebin<strong>(Get, Post vb...)</strong> <strong>type niteliğinde(attribute) </strong>belirtilen <strong>RaporHandler</strong> sınıfına ait nesne örneği tarafından karşılanacağı belirtilmektedir. Bu adım tek başına yeterli değildir. Ayrıca <strong>IIS(Internet Information Services</strong>) üzerinden, rapx uzantılı dosyalara gelecek olan talepler için <strong>AspNet_Isapi.dll</strong>' inin devreye gireceğinin belirtilmesi gerekmektedir. Örnek senaryo <strong>IIS 7.0</strong> üzerinde geliştirilmektedir. IIS 7.0 üzerinden <strong>OzelHandlerKullanimi</strong> isimli web uygulaması adına<strong> rapx-AspNet_Isapi.dll</strong> eşleştirmesi için öncelikli olaran <strong>InetMgr</strong> aracı üzerinden ilgili web uygulamasına geçilmeli ve <strong>Handler Mappings</strong> kısmı aşağıdaki ekran görüntüsünde olduğu gibi seçilmelidir.</p>
<p><img src="/makale/images/mk234_2.gif" alt="" width="630" height="485" border="0" /></p>
<p><strong>Handler Mappings</strong> kısmında varsayılan ve izin verilen uzantı eşleştirmeleri yer almaktadır. Bu kısımda sağ tıklanarak açılan menüden <strong>Add Script Map</strong> seçilmeli ve gerekli eşleştirme IIS tarafına bildirilmelidir.</p>
<p><img src="/makale/images/mk234_3.gif" alt="" width="620" height="480" border="0" /></p>
<p><strong>Add Script Map</strong> ile açılan iletişim kutusunda ise aşağıdaki ayarların yapılması gerekmektedir.</p>
<p><img src="/makale/images/mk234_4.gif" alt="" width="471" height="382" border="0" /></p>
<p>Buna göre <strong>rapx</strong> uzantılı taleplerin <strong>.Net 2.0 </strong>çalışma zamanında yer alan <strong>AspNet_Isapi.dll </strong>tarafından ele alınacağı belirtilmektedir. Uygulamaya ait <strong>web.config</strong> dosyası içerisinde gerekli Handler tanımlamaları yapıldığı için <strong>AspNet_Isapi.dll</strong> program arayüzü, gelen <strong>talep(Request)</strong> için <strong>RaporHandler</strong> sınıfının devreye girmesini sağlayacaktır. Bu işlemin ardından <strong>Handler</strong> <strong>Mappings</strong> kısmına aşağıdaki ekran görüntüsünde de görüldüğü gibi rapx uzantısı için gerekli eşleştirme eklenecektir.</p>
<p><img src="/makale/images/mk234_5.gif" alt="" width="532" height="301" border="0" /></p>
<p>Yapılan bu değişikliker sonrasında web uygulmasında ait <strong>web.config</strong> dosyası içerisindede aşağıdaki gibi yeni bir tanımlama oluşacaktır. Bir başka deyişle <strong>IIS(Internet Information Services)</strong> üzerinde yapılan eşleştirmeye ait bilgiler <strong>system.webServer</strong> elementi içerisindeki <strong>handlers</strong> kısmına eklenmektedir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appSettings />
<connectionStrings />
<system.web>
<httpHandlers>
<add path="*.rapx" type="ReportHandlerLibrary.RaporHandler,ReportHandlerLibrary" verb="*" validate="true" />
</httpHandlers>
<compilation debug="true" />
<authentication mode="Windows" />
</system.web>
<system.webServer>
<handlers>
<add name="Rapor X" path="*.rapx" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\ Framework\v2.0.50727\aspnet_isapi.dll" resourceType="File" />
</handlers>
</system.webServer>
</configuration></pre>
<p>Artık test amacıyla web uygulamasına <strong>rapx</strong> uzantılı örnek bir içerik eklenebilir. Örnek olarak <strong>AdventureWorks</strong> veritabanında yer alan <strong>Products</strong> tablosu ile ilişkili bir rapor düşünülebilir. Bu raporun stok seviyesi belirli bir değerin altında olan ürünlerin kategori bazlı sayılarını verdiğini düşünelim. Buna göre web uygulaması altında tutulacak olan rapx uzantılı metin dosyasının içeriği aşağıdaki gibi tasarlanabilir.<em>(Bu dosyayı eklerken <strong>Add New Item-Text File</strong> seçerek <strong>KategoriBazliUrunler.rapx </strong>gibi bir seçim yapılması gerektiğine dikkat edilmelidir.)</em></p>
<p><strong>KategoriBazliUrunler.rapx;</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<Raporlar>
<Rapor Id="1">
<Baslik ArkaPlan="#FFCC11">Kategori Bazlı Ürün Sayıları</Baslik>
<Baglanti Sspi="true">
<Sunucu>localhost</Sunucu>
<Veritabani>AdventureWorks</Veritabani>
<KullaniciAdi></KullaniciAdi>
<Sifre></Sifre>
</Baglanti>
<Sorgu Sp="false">
<Cumle> Select Count(P.ProductID) [Toplam Ürün Sayısı],PSC.Name [Kategori Adı] From Production.Product P Join Production.ProductSubCategory PSC On P.ProductSubCategoryID=PSC.ProductSubCategoryID Group By PSC.Name,SafetyStockLevel Having P.SafetyStockLevel<@StockLevel
</Cumle>
<Parametreler Nereden="Xml">
<Parametre Ad="@StockLevel" Deger="10"/>
</Parametreler>
</Sorgu>
<MailListesi MailGonder="true">
<Mail>selim(at)buraksenyurt.com</Mail>
<Mail>bsenyurt@csharpnedir.com</Mail>
</MailListesi>
</Rapor>
</Raporlar></pre>
<p>Burada basit olarak <strong>Product</strong> ve <strong>ProductSubCategory</strong> tablolarının <strong>Join</strong> ile birleştirilmiş hali üzerinden bir gruplama sorgusu gerçekleştirilmektedir. Sorgular <strong>StockLevel</strong> parametresinin değeri 10'dan küçük olanlar için yapılmaktadır. Parametrenin değerinin <strong>XML</strong> içerisinden geleceğini belirtmek için Nereden niteliğine <strong>XML</strong> değeri atanmıştır. Söz konusu rapor için <strong>MailListesinde</strong> belirtilen kişilere mail gönderme opsiyonu açık bırakılmıştır. Rapor, <strong>localhost</strong> isimli sunucudaki <strong>AdventureWorks</strong> veritabanı üzerinden <strong>SSPI</strong> ile gerçekleştirilen bir bağlantı üzerinden alınmaktadır. Artık <strong>OzelHandlerKullanimi</strong> adresi üzerinden <strong>KategoriBazliUrunler.rapx</strong> dosyasına bir talepte bulunulursa aşağıdaki gibi bir sonuç ortaya çıkacaktır.</p>
<p><img src="/makale/images/mk234_6.gif" alt="" width="515" height="721" border="0" /></p>
<p>Görüldüğü gibi sorgu sonucu elde edilen rapor ekrana basit bir tablo olarak basılmıştır. Mail gönderme seçeneği aktif olduğu içinde <strong>Raporu Mail Olarak Gönder</strong> başlıklı linkte sayfa çıktısında yer almaktadır. Bu linke basıldığı takdirde sunucuya yeni bir talep daha gönderilecektir. Şimdilik sadece ilgili MailListesi elementinin içeriği değerlendirilmiştir. Aşağıdaki ekran görüntüsünde bu durum gösterilmektedir.</p>
<p><img src="/makale/images/mk234_12.gif" alt="" width="556" height="173" border="0" /></p>
<p>Yeni bir test ile devam edelim. Bu kez raporun parametrelerine ait değerlerin <strong>QueryString</strong> yardımıyla geldiğini göz önüne alalım. Ayrıca raporun sonuçlarının <strong>Northwind</strong> veritabanı altında yer alan <strong>SalesByCategory</strong> isimli <strong>saklı yordam(stored procedure)</strong> üzerinden elde edildiğini düşünelim. MailGonderme seçeneği yine aktif olsun. Bu durumda <strong>rapx</strong> dosyasının içeriğinin aşağıdaki gibi tasarlanması gerekecektir.</p>
<p><strong>KategoriBazliSatislar.rapx;</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<Raporlar>
<Rapor Id="1">
<Baslik ArkaPlan="#CCBB77">Kategori Bazlı Satışlar</Baslik>
<Baglanti Sspi="true">
<Sunucu>localhost</Sunucu>
<Veritabani>Northwind</Veritabani>
<KullaniciAdi></KullaniciAdi>
<Sifre></Sifre>
</Baglanti>
<Sorgu Sp="true">
<Cumle>SalesByCategory</Cumle>
<Parametreler Nereden="QueryString">
<Parametre Ad="@CategoryName"/>
<Parametre Ad="@OrdYear"/>
</Parametreler>
</Sorgu>
<MailListesi MailGonder="false">
<Mail>selim(at)buraksenyurt.com</Mail>
<Mail>bsenyurt@csharpnedir.com</Mail>
</MailListesi>
</Rapor>
</Raporlar></pre>
<p>Buna göre tarayıcı penceresinden <strong>http://localhost/OzelHandlerKullanimi/KategoriBazliSatislar.rapx?CategoryName=Beverages&OrdYear=1999</strong> adresi talep edilirse aşağıdakine benzer bir ekran görüntüsü ile karşılaşılacaktır.</p>
<p><img src="/makale/images/mk234_7.gif" alt="" width="625" height="517" border="0" /></p>
<p>Elbette uygulamada pek çok hata göz ardı edilmektedir. Söz gelimi <strong>KategoriBazliSatislar</strong>.<strong>rapx</strong> için <strong>QueryString</strong> parametreleri kullanılmassa <strong>çalışma zamanı hataları(Run time exceptions)</strong> alınması çok doğaldır. Bu gibi hataların ele alınması bir başka deyişle kodun tekrardan revize edilerek düzenlenmesi gerekmektedir.</p>
<p>Gelelim <strong>Handler</strong> tipinin sunucu üzerinde nasıl ele alınabileceğine. Bunun için geliştirilen Handler tipini içeren <strong>assembly</strong>' ın <strong>Global Assembly Cache(GAC)</strong> içerisine atılması ve <strong>root web.config </strong>dosyası içerisindeki <strong>httpHandlers</strong> elementi altında bildirilmesi yeterlidir. Örneğimizde geliştirdiğimiz <strong>ReportHandlerLibraray.dll </strong>isimli <strong>assembly</strong>' ı <strong>GAC'a</strong> atmadan önce <strong>Visual Studio 2005</strong> ortamında aşağıdaki gibi <strong>Strong Name</strong> ile imzalamamız gerekmektedir. <em>(İstenirse bu imzalama işlemi komut satırından <strong> sn.exe </strong>aracı ilede gerçekleştirilebilir.) </em></p>
<p><img src="/makale/images/mk234_9.gif" alt="" width="553" height="478" border="0" /></p>
<p>Bu işlemin ardından <strong>ReportHandlerLibrary</strong>' in <strong>gacutil.exe</strong> aracı yardımıyla yada sürükle bırak tekniği ile <strong>Windows\Assembly</strong> klasörüne atılarak <strong>GAC</strong>' a eklenmesi yeterlidir.</p>
<table id="table181" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td valign="top" width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><em>Burada ReportHandlerLibrary projesinin <strong>Relase</strong> modda üretiminin yapılarak<strong>, Output Path</strong> olarak çıktının <a href="file:///C:/Windows/Microsoft.NET/Framework/v2.0.50727/"> <strong>C:\Windows\Microsoft.NET\Framework\v2.0.50727\</strong></a> klasörünü işaret edecek şekilde düzenlenmesi ve daha sonra buradan <strong>GAC</strong>' a atılmasıda tercih edilebilir.</em></td>
</tr>
</tbody>
</table>
<p><br />Sonuç olarak <strong>Windows\Assembly</strong> klasörü altına <strong>ReportHandlerLibrary</strong>' si aşağıdaki ekran görüntüsünde olduğu gibi eklenmiş olacaktır.</p>
<p><img src="/makale/images/mk234_10.gif" alt="" width="514" height="239" border="0" /></p>
<p>Şimdi tek yapılması gereken bu <strong>assembly</strong>' ın <strong>root</strong> <strong>web.config</strong> içerisinde bildirilmesidir. Bunun için <strong>C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG</strong> klasöründe yer alan web.config dosyasına aşağıdaki gibi handler bildiriminin yapılması gerekmektedir.</p>
<p><img src="/makale/images/mk234_11.gif" alt="" width="577" height="160" border="0" /></p>
<p>Bu işlemlerin ardından <strong>IIS</strong> üzerinden <strong>rapx</strong> uzantılı dosyalara gelecek olan taleplerin <strong>AspNet_Isapi.dll </strong>tarafından ele alınacağınında belirtilmesi gerekir. Makalemizin başında <strong>OzelHandlerKullanimi</strong> isimli web sitesi için yaptığımız bu işlemin aynısı bu kez <strong>root web sitesi</strong> için benzer şekilde yapılarak gerçekleştirilmelidir. Sonuç itibariyle sunucu üzerinde <strong>Asp.Net</strong> <strong>2.0 </strong>ile geliştirilen herhangibir web sitesi altında tasarlanacak olan tüm <strong>rapx</strong> uzantılı dosyalar <strong>RaporHandler</strong> sınıfı tarafından ele alınabilecektir.</p>
<blockquote>
<p>Kendi handler tipimizi <strong> root web.config</strong> dosyasında tanımladığımızda, web uygulaması içerisinde yer alan web.config dosyası içerisinde bırakılan handler tanımlamaları nedeni ile <strong>çalışma zamanı hataları(Run time exceptions) </strong>alınır. Bu nedenle makaledeki örnekte yer alan OzelHandlerKullanimi sitesindeki <strong>rapx</strong> dosyaları, root web.config' de yapılan handler bildirimleri sonrası çalışmayacaktır.</p>
<p><img src="/makale/images/mk234_13.gif" alt="" width="492" height="717" border="0" /></p>
<p>Sebep handler tanımlamasının hem root web.config hemde site içerisindeki web.config'de iki kez yapılmış olmasıdır. Bunu düzeltmek için OzelHandlerKullanimi sitesine ait web.config içerisinde yapılan handler bildirimlerini kaldırmak gerekmektedir.</p>
</blockquote>
<p>Kendi <strong>HttpHandler</strong> tiplerimizi tasarlamak için ele aldığımız örnek senaryo çok daha fazla geliştirilebilir. Hatta bir web sunucusu üzerinde konuşlandırılacak olan <strong>rapx</strong> dosyalarının daha kolay hazırlanabilmesini sağlamak için görsel bir arabirimde geliştirilebilir. Sadece Rapx uzantılı dosyaları barındıracak olan bir <strong>Asp.Net</strong> uygulaması geliştirilerek tüm raporların tek bir merkezde toplanması sağlanabilir. Hatta bu işi dahada ileriye götürecek olursak, raporların yetki tabanlı olacak şekilde ele alınabilmesi için gerekli hazırlıklarda yapılabilir. Bu noktada, tasarlanan bu sistemin <strong>Reporting Service</strong> alt yapısına ne kadar benzediğini tartışmakta yarar vardır. Sistemin herhangibir veritabanına destek verecek şekilde ele alınması ise çok daha etkili bir raporlama arayüzü oluşturulmasına ön ayak olacaktır.</p>
<p>Bu makalemizde daha önceden ele aldığımız ancak geçerli bir ihtiyaç veya senaryo üzerine oturtamadığımız <strong>HttpHandler</strong> kavramını bir örnek üzerinde adım adım incelemeye çalıştık. Sizlerde farklı senaryoları göz önüne alarak ve makalede yer alan örneği dahada geliştirirek son derece etkili sonuçlara varabilirsiniz. Böylece geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/makale/images/OzelHandler.rar">Örnek Uygulama için Tıklayın</a></p>2007-12-10T12:00:00+00:00asp.net 2.0http handlerasp.netbsenyurtUzun zaman önce Asp.Net 2.0 ile ilişkili makalelerimizden birisinde HttpHandler ve HttpModule kavramlarından bahsetmeye çalışmıştık. Bu makalemizde kendi Handler sınıfımızı geliştirmek isteyebileceğimiz örnek bir senaryo üzerinde daha durmaya çalışacağız. Bu sayede HttpHandler sınıfları yazarak neler yapılabileceğinide daha net bir şekilde görmüş olacağız. Konuyu daha net kavrayabilmek adına örnek senaryomuz üzerinden adım adım ilerleyeceğiz.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=63e8d206-9a7e-478a-8866-70b76f3c21dc0https://buraksenyurt.com/trackback.axd?id=63e8d206-9a7e-478a-8866-70b76f3c21dchttps://buraksenyurt.com/post/Nasc4b1l-Yapc4b1lc4b1r-Adc4b1m-Adc4b1m-Ozel-HttpHandler-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=63e8d206-9a7e-478a-8866-70b76f3c21dchttps://buraksenyurt.com/post/Daha-Etkili-Profil(Profile)-Yonetimi-bsenyurt-com-danDaha Etkili Profil(Profile) Yönetimi2007-10-17T12:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Uzun süre önce Asp.Net 2.0 ile geliştirilen web uygulamalarında <strong>Profile API</strong>' sinin nasıl kullanıldığını kısa bir <a href="http://www.bsenyurt.com/MakaleGoster.aspx?ID=160">makale</a> üzerinden incelemeye çalışmıştık. Geçtiğimiz günlerde Asp.Net 2.0 ile ilgili bilgilerimi tazelerken profil yönetiminin daha etkin bir şekilde nasıl kullanılabileceğine dair pek çok örnek ile karşılaştım. İşte bu makalemizde temel olarak profil yönetiminin daha etkin hale getirilmeye çalışması için uğraşıyor olacağız. İnceleyeceğimiz temel konu başlıklarını aşağıdaki gibi sıralayabiliriz.</p>
<ul>
<li><strong>ProfileBase</strong> tipinden türetmek(Inherit).</li>
<li>Profil bilgilerini kod üzerinden yönetebilmek<strong>(ProfileManager)</strong>.</li>
<li><strong>İsimsiz(Anonymous)</strong> kullanıcılar için profil bilgilerini kullanabilmek.</li>
</ul>
<p>Başlamadan önce profil kavramını kısaca tanımlamakta yarar olduğu kanısındayım. Bir web uygulamasına bağlanan kullanıcıların her biri için ortak tanımlanıp değerleri farklı olabilecek özellikler topluluğu profil bilgisini oluşturmaktadır. Bu anlamda özellikle, bir <strong>doğrulama(authentication) </strong>ve <strong> yetkilendirme(authorization)</strong> sistemine sahip olan web uygulamalarında her kullanıcı için değerleri farklı olabilecek özelliklerin tutulması ve kullanılması mümkün olabilmektedir. Bu tip bir sistemin özellikle <strong>Asp.Net 1.1</strong> ile geliştirilmesi ekstra kodlamayı gerektirirken Asp.Net 2.0 üzerinde yer alan Profile API sayesinde son derece kolaylaşmıştır. Gelelim Profile API yeteneklerini daha etkili bir şekilde nasıl ele alabileceğimize.</p>
<p><strong>ProfileBase Tipinden Türetmek(Inherit);</strong></p>
<p>Normal şartlarda bir web uygulaması içerisinde profil bilgilerini kullanabilmek için <strong>web.config</strong> dosyası içerisinde <strong>profile</strong> elementinin ele alınması gerekmektedir. Nitekim bir web uygulamasında kullanılan profil bilgilerinin, başka web uygulamasında(web uygulamalarında) ele alınmasının istendiği vakkalarda mevcuttur. Bu tip bir durumda çözüm olarak, <strong>ProfileBase</strong> tipinden türetme yapılaraktan birden fazla web uygulamasında ele alınabilecek bir profil sınıfı geliştirmek mümkündür. ProfileBase sınıfının temel üyeleri aşağıdaki sınıf diagramında(Class Diagram) görüldüğü gibidir.</p>
<p><img src="/makale/images/mk227_1.gif" alt="" width="459" height="552" border="0" /></p>
<p><strong>PorfileBase</strong> tipi sınıf diagramından(class diagram) da görüldüğü gibi <strong>SettingBase</strong> isimli abstract sınıftan(class) türemeketedir. ProfileBase tipine ait üyelerden bazılarının görevleri aşağıdaki tabloda belirtildiği gibidir.</p>
<table style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td style="background-color: #ffffff;" valign="top" bgcolor="#336699"><strong> Metodlar(Methods)</strong></td>
<td style="background-color: #ffffff;" valign="top" bgcolor="#336699"><strong> Açıklama</strong></td>
</tr>
<tr>
<td valign="top"><strong>Create</strong></td>
<td valign="top">Bu metod ile bir kullanıcı için profil nesne örneği oluşturulur. Özel profil tiplerinin yazılmasında veya Asp.Net ortamı dışındaki çevrelerde profil yönetimi söz konusu olduğunda ele alınmaktadır. Metod geriye <strong>ProfileBase</strong> tipinin taşıyabileceği referansları döndürür. İki farklı versiyonu vardır. Her iki versiyonda ilk parametre olarak kullanıcı adını alır. İkinci parametre <strong>bool</strong> bir değerdir ve kullanıcının<strong> isimsiz(anonymous) </strong>yada <strong> doğrulanmış(authenticated)</strong> olup olmadığını belirtir. Metodun dönüş değerinin true olması kullanıcının doğrulandığı anlamına gelmektedir.</td>
</tr>
<tr>
<td valign="top"><strong>Save</strong></td>
<td valign="top">Profil bilgilerini kaydetmek amacıyla kullanılır. Herhangibir parametre almaz ve geriye değer döndürmez(void). Var olan bir <strong> ProfileBase</strong> nesne örneği üzerinden çağrılabildiği için ilgili tipe ait özelliklerde yapılan değişikliklerin kaydedilmesini sağlar. Kaydetme işlemi veri kaynağına<strong>(data source)</strong> doğru yapılmaktadır. Bu işlem sırasında <strong>IsDirty</strong> özelliği true değerini alır. İşlem tamamlandıktan sonra ise <strong> false</strong> değerini alır.</td>
</tr>
<tr>
<td valign="top"><strong> GetPropertyValue</strong></td>
<td valign="top">Parametre olarak verilen özelliğin değerini object tipinden döndürür.</td>
</tr>
<tr>
<td valign="top"><strong> SetPropertyValue</strong></td>
<td valign="top">İki parametre alan bu metodun ilk parametresi değeri verilecek özellik adını, ikinci parametresi ise object tipinden ilgili değeri almaktadır. Bu metod yardımıyla profil içerisindeki bir özelliğe değer atanabilmesi sağlanabilmektedir.</td>
</tr>
<tr>
<td valign="top"><strong> GetProfileGroup</strong></td>
<td valign="top">Profil içerisinde yer alan özellikler istenirse grup halinde ayrılabilirler. Bunun için profile elementi içerisinde yer alan <strong>properties</strong> elementlerinde, <strong>group</strong> alt elementi kullanılmaktadır. Böyle bir durumda gruplanan özelliklerin listesini elde etmek için <strong>GetProfileGroup</strong> metodu kullanılabilir. Bu metod geriye <strong>ProfileGroupBase</strong> tipinden bir referans döndürmektedir. Bu referansın üzerinden hareket ederek grup içerisindeki özelliklere ve değerlerine erişmek mümkün olmaktadır. Aşağıdaki sınıf diagramı görüntüsünde ProfileGruopBase sınıfının üyeleri görülmektedir.
<p><img src="/makale/images/mk227_2.gif" alt="" width="442" height="210" border="0" /></p>
</td>
</tr>
<tr>
<td style="background-color: #ffffff;" valign="top" bgcolor="#336699"><strong> Özellikler(Properties)</strong></td>
<td style="background-color: #ffffff;" valign="top" bgcolor="#336699"><strong> Açıklama</strong></td>
</tr>
<tr>
<td valign="top"><strong>Item</strong></td>
<td valign="top">Profil içerisinde tanımlanmış olan özellik adlarını parametre olarak alabilen indeksleyici sayesinde ilgili özelliğin değerinin <strong> verilmesi(set)</strong> veya <strong>elde edilmesi(get) </strong>mümkündür. Özellik adı string tipinden verilmekte olup indeksleyicinin dönüş değeri object tipindendir.</td>
</tr>
<tr>
<td valign="top"><strong>Properties</strong></td>
<td valign="top"><strong>Static</strong> olarak tanımlanmış olan bu özellik sayesinde profil özelliklerinin bir listesinin <strong>SettingsPropertyCollection</strong> koleksiyon tipinden elde edilmesi mümkündür. Bu koleksiyonun her bir elemanı <strong>SettingsProperty</strong> sınıfı tipindendir. Bu tipin üyeleri ise aşağıdaki sınıf diagramında görüldüğü gibidir. Dikkat edilecek olursa bu üyelerden yola çıkarak profilin özelliği hakkında detaylı bilgilere ulaşmak veya yönetmek mümkündür.
<p><img src="/makale/images/mk227_3.gif" alt="" width="276" height="310" border="0" /></p>
</td>
</tr>
<tr>
<td valign="top"><strong>UserName</strong></td>
<td valign="top">Profilin sahibi olan kullanıcı adını verir. Eğer isimsiz bir kullanıcı girişi söz konusu ise <strong>identifier</strong> değerini döndürecektir.</td>
</tr>
<tr>
<td valign="top"><strong>IsDirty</strong></td>
<td valign="top">Profil özelliklerinden herhangibiri değiştiriliyorken <strong>true</strong> değerini döndürür. Aksi durumda false değerini döndürmektedir.</td>
</tr>
<tr>
<td valign="top"><strong>IsAnonymous</strong></td>
<td valign="top">Eğer kullanıcı <strong> isimsiz(anonymous)</strong> ise <strong>true</strong> değerini döndürür. Aksi durumda false' dur.</td>
</tr>
</tbody>
</table>
<p>Şimdi örnek bir senaryo üzerinden hareket ederek konuyu biraz daha iyi kavramaya çalışalım. Öncelikli olarak hedefimiz birden fazla web uygulamasının kullanabileceği bir <strong> Profile</strong> tipi geliştirmek olduğundan bir <strong>sınıf kütüphanesi(class library)</strong> geliştirerek işe başlanabilir. Çok doğal olarak bu sınıf kütüphanesi içerisinde <strong>ProfileBase</strong> tipi kullanılacağından ve yeri geldiğinde güncel HTTP içeriğine<strong>(HttpContext)</strong> erişilmesi gerektiğinden <strong>System.Web.dll</strong> assembly' ının ilgili sınıf kütüphanesine referans edilmesi gerekmektedir.</p>
<p>Özel olarak hazırlanacak sınıfın sağlaması gereken bazı özellikler vardır. İlk olarak bu sınıfın en azından XML serileştirilebilir<strong>(XML Serializable)</strong> olması gerekmektedir. Çok doğal olarak bu sınıf içerisinde kullanılacak özelliklerin <strong>veri tipleride(data types) </strong>serileştirilebilir olmalıdır. <em>(Kendi tiplerimizden özellik türleri yazmadığımızda çoğunluklu ilkel tipleri(primitive types) kullanırız. Bu tiplerin çoğu zaten serileştirilebilir olduğundan sorun çıkma olasılığı azalmaktadır. Ancak kendi tiplerimizi ele aldığımızda serileştirilebilir olmalarına dikkat etmek gerekmektedir.) </em>İkinci olarak özel tip içerisinde, web uygulamalarındaki kullanıcılar için gerekli profil bilgisini oluşturacak <strong>özellikler(property) </strong>ayrı ayrı tanımlanmalıdır. Bu özelliklerin kullanımı sırasında base anahtar kelimesi ile üst sınıfa aktarma yapılmasına dikkat edilmelidir. Üçünü olarak elbetteki özel sınıfın <strong> ProfileBase</strong> tipinden türetilmiş olması gerekmektedir. Bu türetmenin doğal sonucu olarak ProfileBase sınıfı içerisinde tanımlanmış bazı üyelerin <strong>ezilebileceği(override) </strong>ortadadır.</p>
<blockquote>
<p>Hatırlanacağı üzere <strong>üst sınıfta(base class)</strong> <strong>sanal(virtual) </strong>olarak tanımlanış olan üyelerin, <strong>türeyen sınıflarda(derived class)</strong> <strong>ezilme(override)</strong> zorunluluğu yoktur. Eğer bir zorunluluk getirilmesi isteniyorsa <strong>abstract üyelerin</strong> yer alabildiği <strong>abstract sınıflar</strong> veya <strong>arayüzler(interface)</strong> kullanılmalıdır.</p>
</blockquote>
<p>Aşağıdaki şekilde <strong>türeyen sınıf(derived class)</strong> içerisinde ezilebilecek üyeler gösterilmektedir. Doğal olarak herkes bir Object olduğundan, object sınıfından gelen bazı virtual üyelerde bu listede yer almaktadır.</p>
<p><img src="/makale/images/mk227_4.gif" alt="" width="347" height="217" border="0" /></p>
<p>Örnek olarak MyProfile sınıfı aşağıdaki gibi tasarlanabilir. Sınıf içerisinde kontak bilgilerini saklamak amacıyla <strong>Contact</strong> isimli bir sınıf daha kullanılmaktadır. Bu sınıfa ait nesne örneklerini kullanan özellikler MyProfile sınıfı içerisinde ele alınarak, kullanıcı tanımlı tiplerin durumuda rahatlıkla incelenebilir.</p>
<p><em><strong>Contact Sınıfı;</strong></em></p>
<p><img src="/makale/images/mk227_8.gif" alt="" width="311" height="302" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class Contact
{
private string _ad;
private string _soyad;
private string _email;
public string Email
{
get { return _email; }
set { _email = value; }
}
public string Soyad
{
get { return _soyad; }
set { _soyad = value; }
}
public string Ad
{
get { return _ad; }
set { _ad = value; }
}
public Contact(string ad, string soyad, string mail)
{
Ad = ad;
Soyad = soyad;
Email = mail;
}
// XML Serileştirme kuralı olarak parametresiz bir yapıcı metod(constructor) olması gerekmektedir.
public Contact()
{
}
}</pre>
<p>Burada dikkat edilmesi gereken noktalardan biriside Contact sınıfının <strong>varsayılan yapıcı metodunun(Default Constructor)</strong> yazılmış olmasıdır. Profil bilgilerinde kendi tiplerimizi kullandığımız durumlarda varsayılan olarak XML serileştirme gerçekleştirilmektedir. Dolayısıyla yine varsayılan olarak <strong>AspNetDb</strong> veritabanındaki <strong>aspnet_Profile</strong> tablosuna yazılacak olan özellik değerlerinin XML formatında serileştirilebilir olması gerekmektedir. Bu sebepten ilgili tipin varsayılan yapıcı metoda sahip olması gerekir ki bu XML serileştirmenin kurallarından birisidir. Eğer varsayılan yapıcı metodu yazmassak çalışma zamanında profil bilgisini kaydederken aşağıdaki ekran görüntüsünde yer alan <strong>istisnayı(exception)</strong> alırız.</p>
<p><img src="/makale/images/mk227_6.gif" alt="" width="474" height="277" border="0" /></p>
<p> </p>
<p><em><strong>MyProfile Sınıfı;</strong></em></p>
<p><img src="/makale/images/mk227_9.gif" alt="" width="335" height="432" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class MyProfile:ProfileBase
{
public int SonStokDurumu
{
get { return Convert.ToInt32(base["SonStokDurumu"]); }
set { base["SonStokDurumu"] = value; }
}
public DateTime SonGirisZamani
{
get { return Convert.ToDateTime(base["SonGirisZamani"]); }
set { base["SonGirisZamani"] = value; }
}
public Contact Contact2
{
get { return (Contact)base["Contact2"]; }
set { base["Contact2"] = value; }
}
public Contact Contact1
{
get { return (Contact)base["Contact1"]; }
set { base["Contact1"] = value; }
}
public string Bayi
{
get { return base["Bayi"].ToString(); }
set { base["Bayi"] = value; }
}
}</pre>
<p>Dikkat edilecek olursa sınıf içerisinde tasarlanan özelliklere ait <strong>get</strong> ve <strong>set</strong> bloklarında <strong>base</strong> anahtar kelimesi ile <strong>ProfileBase</strong> sınıfına çıkılmakta ve indeksleyici operatöründen yararlanılarak ilgili alanlara erişilmesi sağlanmaktadır. Elbetteki base ile ulaşılan referansın özellikleri object veri türü ile çalıştığından set blokları içerisinde uygun tür dönüşümlerinin <strong>bilinçli(explicit)</strong> olarak yapılması şarttır. MyProfile sınıfı içerisinde tanımlanacak <strong>özel alanlara(private fields)</strong> değer atanması durumunda veritabanına herhangibir şekilde bilgi yazılmayacaktır. Böyle bir işlem için <strong>Save</strong> metodunun bu sınıf içerisinde ezilemesi ve kodlanması gerekmektedir.</p>
<p>Şimdi MyProfile sınıfını test etmek amacıyla basit bir web uygulaması tasarlayalım. Bu web uygulamasında standart olarak <strong>AspNetDb</strong> veritabanı kullanılabilir. <strong>Form tabanlı doğrulama(Form Based Authentication)</strong> sisteminin yer aldığı web uygulamasında standart olarak Login.aspx sayfası kullanıcı girişi için kullanılırken, Default.aspx sayfası içerisinde örnek test kodları yer almaktadır. Web uygulamasına ait konfigurasyon dosyasının<strong>(Web.config)</strong> içeriği aşağıdaki gibi olmalıdır.</p>
<p><em><strong>web.config;</strong></em></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<compilation debug="true" />
<authentication mode="Forms" />
<authorization>
<deny users="?"/>
</authorization>
<profile enabled="true" inherits="CustomProfileLibrary.MyProfile"/>
</system.web>
</configuration></pre>
<p>profile elementi içerisinde <strong>inherits</strong> niteliğine(attribute) atanan değer ile <strong>Profile</strong> tipinin ne olduğu söylenir. Burada <strong>İsimAlanıAdı.TipAdı(NamespaceName.TypeName)</strong> notasyonu kullanılarak profile bilgilerinin yönetiminin MyProfile sınıfı tarafından yapılacağı belirtilmektedir. Form tabanlı doğrulama(Form Based Authentication) kullanıldığından <strong>authentication</strong> elementinin <strong>mode</strong> niteliğine <strong>Forms</strong> değeri atanmıştır. <strong>İsimsiz kullanıcıların(anonymous users) </strong>siteye giriş yapması istenmediğinden <strong>authorization</strong> elementi içerisinden söz konusu kullanıcılar <strong>? </strong>karakteri ile <strong>deny</strong> edilmiştir. Gelelim uygulamanın örnek web sayfasına. Sayfanın tasarımı aşağıda görüldüğü gibidir.</p>
<p><em><strong>Default.aspx sayfası;</strong></em></p>
<p><img src="/makale/images/mk227_10.gif" alt="" width="403" height="419" border="0" /></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:LoginName ID="LoginName1" runat="server" /><asp:LoginStatus ID="LoginStatus1" runat="server" />
<br />
<table>
<tr>
<td colspan="2" valign="top">Profil Özellikleri <asp:Button ID="btnGetir" runat="server" OnClick="btnGetir_Click" Text="Bilgileri Getir" /></td>
</tr>
<tr>
<td style="width: 164px" valign="top">Bayi</td>
<td style="width: 100px"><asp:TextBox ID="txtBayi" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 164px" valign="top">Kontak 1</td>
<td style="width: 100px">
<table>
<tr>
<td style="width: 100px">Ad</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak1Ad" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 100px">Soyad</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak1Soyad" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 100px">Email</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak1Email" runat="server"></asp:TextBox></td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="width: 164px" valign="top">Kontak 2</td>
<td style="width: 100px">
<table>
<tr>
<td style="width: 100px">Ad</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak2Ad" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 100px">Soyad</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak2Soyad" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 100px">Email</td>
<td style="width: 100px"><asp:TextBox ID="txtKontak2Email" runat="server"></asp:TextBox></td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="width: 164px; height: 10px" valign="top">Son Giriş Zamanı</td>
<td style="width: 100px; height: 10px"><asp:Label ID="lblSonGirisZamani" runat="server" Text="Label"></asp:Label></td>
</tr>
<tr>
<td style="width: 164px; height: 26px" valign="top">Stok Durumu</td>
<td style="width: 100px; height: 26px"><asp:TextBox ID="txtStokDurumu" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td colspan="2" valign="top"><asp:Button ID="btnKaydet" runat="server" OnClick="btnKaydet_Click" Text="Bilgileri Kaydet" /></td>
</tr>
</table>
<br />
<br />
</div>
</form>
</body>
</html></pre>
<p><em><strong>Default.aspx.cs;</strong></em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using CustomProfileLibrary;
public partial class _Default : System.Web.UI.Page
{
protected void btnGetir_Click(object sender, EventArgs e)
{
Getir();
}
protected void btnKaydet_Click(object sender, EventArgs e)
{
Kaydet();
}
private void Getir()
{
try
{
txtBayi.Text = Profile.Bayi;
txtKontak1Ad.Text = Profile.Contact1.Ad;
txtKontak1Email.Text = Profile.Contact1.Email;
txtKontak1Soyad.Text = Profile.Contact1.Soyad;
txtKontak2Ad.Text = Profile.Contact2.Ad;
txtKontak2Email.Text = Profile.Contact2.Email;
txtKontak2Soyad.Text = Profile.Contact2.Soyad;
lblSonGirisZamani.Text = Profile.SonGirisZamani.ToString();
txtStokDurumu.Text = Profile.SonStokDurumu.ToString();
}
catch (Exception excp)
{
Response.Write(excp.Message);
}
}
private void Kaydet()
{
Profile.Bayi = txtBayi.Text;
Contact kontak1 = new Contact(txtKontak1Ad.Text, txtKontak1Soyad.Text, txtKontak1Email.Text);
Profile.Contact1 = kontak1;
Contact kontak2 = new Contact(txtKontak2Ad.Text, txtKontak2Soyad.Text, txtKontak2Email.Text);
Profile.Contact2 = kontak2;
Profile.SonGirisZamani = DateTime.Now;
int stok = 1;
Int32.TryParse(txtStokDurumu.Text, out stok);
Profile.SonStokDurumu = stok;
Profile.Save();
}
}</pre>
<p>Sayfa basit olarak profil bilgilerinin getirilmesi veya kaydedilmesi için gereken kodları içermektedir. Dikkat edilirse Profile tipi kullanılmaktadır. Nitekim <strong>web.config </strong> dosyasındaki bildirim nedeni ile <strong>Profile</strong> özelliği üzerinden MyProfile içerisinde tanımlanmış olan özelliklere erişilebilmektedir. Aşağıdaki şekilde bu durum daha net bir şekilde görülebilir.</p>
<p><img src="/makale/images/mk227_5.gif" alt="" width="513" height="330" border="0" /></p>
<p>Çok doğal olarak daha önceden bir profil bilgisi oluşturulmaması durumuna karşılık bilgiler getirilirken <strong>Null Reference Exception</strong> alınma olasılığı vardır. Bu nedenle profil bilgileri ortama çekilirken bir <strong>try...catch</strong> bloğu kullanılması yararlı olabilir. Sayfa üzerinde test bilgilerinin kaydedilmesi sonrasında <strong>aspnet_profile</strong> tablosunun içeriğine bakılırsa profil bilgisinin başarılı bir şekilde kaydedildiği görülebilir. Dikkat edileceği üzere bilgiler XML serileştirmeye uygun olacak şekilde <strong>XML</strong> formatında <strong>PropertyValueString</strong> alanına yazılmıştır.</p>
<p><img src="/makale/images/mk227_7.gif" alt="" width="638" height="63" border="0" /></p>
<p>Çalışma zamanında(run-time) Bilgileri Getir başlıklı düğmeye basıldığında ise söz konusu verilerin ilgili kontrollere doldurulduğu görülecektir. Aşağıda bu duruma ait örnek bir ekran çıktısı yer almaktadır.</p>
<p><img src="/makale/images/mk227_11.gif" alt="" width="411" height="486" border="0" /></p>
<p><strong>Profil Bilgilerini Kod Üzerinden Yönetebilmek;</strong></p>
<p>Bazı durumlarda çalışma zamanında(run-time) uygulamada kullanılan profil bilgilerinin yönetilmesi istenebilir. Söz gelimi profilde kayıtlı bilgilerin gösterilmesi, belirli bir tarihten öncekilerin kaldırılması, bir kullanıcının profil bilgisinin silinmesi, aktif olmayan profillerin elde edilmesi vb... işlemler yapılabilir. Bu aslında basit olarak veritabanına ulaşmak ve ilgili tablonun alanlarına bakmaktan başka bir şey değildir. Ne varki <strong>Asp.Net Profile API</strong> içerisinde söz konusu yönetsel işlemlerin daha kolay yapılmasını sağlayan <strong>ProfileManager</strong> sınıfı mevcuttur. Bu sınıfın diagram görüntüsü aşağıdaki gibidir.</p>
<p><img src="/makale/images/mk227_12.gif" alt="" width="473" height="388" border="0" /></p>
<p>Dikkat edileceği üzere ProfileManager, <strong>static</strong> bir sınıftır(static class).</p>
<blockquote>
<p>Static sınıf kavramı C# 2.0 ile birlikte gelmiştir. Static sınıflar sadece static üyeler<strong>(static members)</strong> içerebilir, <strong>türetme(Inheritance)</strong> amacıyla kullanılamaz veya örneklenemezler. Normal sınıflara göre daha hızlı ve performanslı çalıştıkları ortadadır. Bununla birlikte C# 3.0 ile birlikte gelen <strong>extension methods</strong> kavramında önemli bir yerede sahiptir.</p>
</blockquote>
<p>Sınıfın önemli metodları ve yaptığı işler ise aşağıdaki tabloda görüldüğü gibidir.</p>
<table style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td style="width: 230px; background-color: #ffffff;" valign="top" bgcolor="#336699" width="230"><strong> Metodlar(Methods)</strong></td>
<td style="width: 230px; background-color: #ffffff;" valign="top" bgcolor="#336699"><strong> Açıklama</strong></td>
</tr>
<tr>
<td valign="top" width="230"><strong> GetAllProfiles</strong></td>
<td valign="top">İki versiyonu olan bu metod sayesinde bir uygulamadaki profil bilgilerinin tamamı <strong>ProfileInfoCollection</strong> tipi içerisinde olacak şekilde elde edilebilir. Her iki versiyonda <strong> ProfileAuthenticationOption</strong> enum sabiti tipinden bir parametre almaktadır. Bu parametrenin alabileceği değerler <strong> All, Anonymous, Authneticated</strong>' dır. Bir başka deyişle tüm kullanıcıların, sadece isimsiz kullanıcıların veya sadece doğrulanmış kullanıcıların profile bilgilerinin elde edilmesi sağlanabilir. Ayrıca bu metod ile sayfalamalara uygun olacak şekilde veri çekilmeside sağlanabilmektedir.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> GetNumberOfProfiles</strong></td>
<td valign="top">Veritabanında kayıtlı olan profil sayısının elde edilmesini sağlayan bu metod yine ProfileAuthenticationOption enum sabiti tipinden bir parametre alır. Buna göre sadece <strong>isimsiz kullanıcıların(anonymous users), doğrulanmış kullanıcıların(authenticated users)</strong> yada <strong>tüm kullanıcıların(all users)</strong> kayıtlı olan profil sayıları elde edilebilir.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> GetAllInactiveProfiles</strong></td>
<td valign="top">Profil sahibi kullanıcıların <strong>LastActivityDate</strong> özelliklerinin değerlerine göre parametre olarak verilen tarih ve öncesindeki tüm profillerin elde edilmesini sağlayan metoddur. İki versiyonu vardır ve her ikisinin ilk parametresi aynıdır. İlk parametrede ProfileAuthenticationOption değeri ikincisinde ise tarih bilgisi belirlenir.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> GetNumberOfInactiveProfiles</strong></td>
<td valign="top">Profil sahibi kullanıcıların <strong>LastActivityDate</strong> özelliklerinin değerlerine göre parametre olarak verilen tarih ve öncesindeki tüm profillerin sayısının integer olarak elde edilmesini sağlayan metoddur.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> FindProfilesByUserName</strong></td>
<td valign="top">Belirlenen kullanıcı adıyla eşleşen profil bilgilerinin <strong> ProfileInfoCollection</strong> tipinden döndürülmesini sağlamaktadır. İki farklı versiyonu vardır. Her ikiside ilk iki parametresinde sırasıyla <strong>ProfileAuthenticationOption</strong> ve kullanıcı adı bilgilerini almaktadır. Sayfalama yapılmasıda sağlanabilmektedir. Burada kullanıcı adı girilirken <strong>%</strong> karakteri kullanılarak <strong>Like</strong> benzeri bir sorgulama yapılabilmektedir.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> FindInactiveProfilesByUserName</strong></td>
<td valign="top">Profil sahibi kullanıcıların <strong>LastActivityDate</strong> özelliklerinin değerlerine göre belirtilen tarih ve öncesinde olanların parametre olarak verilen kullanıcı adı ile eşleşenlerinin bulunmasını sağlayan metoddur. Burada kullanıcı adı girilirken % karakteri kullanılarak Like benzeri bir sorgulama yapılabilmektedir. Örneğin kullanıcı adı içerisinde ma geçenlerin elde edilip belirtilen bir tarhiten öncesine kadar aktivitesi olmayanlar listelenebilir.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> DeleteProfile</strong></td>
<td valign="top">Parametre olarak verilen kullanıcıya ait profile bilgisinin silinmesini sağlamaktadır. Bu metod silme işlemi başarılı ise <strong>true</strong> değerini döndürür.</td>
</tr>
<tr>
<td valign="top" width="230"><strong> DeleteProfiles</strong></td>
<td valign="top">Bu metodun iki farklı versiyonu vardır. Bunlardan birisi <strong> ProfileInfoCollection</strong> tipinden diğeri ise <strong>string</strong> dizisi tipinden parametre almaktadır. Dolayısıyla profile bilgilerinin silinmesi istenen kullanıcı tipleri iki farklı şekilde yüklenebilir. </td>
</tr>
<tr>
<td valign="top" width="230"><strong> DeleteInactiveProfiles</strong></td>
<td valign="top">Profil sahibi kullanıcıların <strong>LastActivityDate</strong> özelliklerinin değerlerine göre belirtilen tarih ve öncesinde olanların silinmesini sağlayan metod ProfileAuthenticationOption enum sabiti tipinden değerde almaktadır.</td>
</tr>
</tbody>
</table>
<p>Şimdi bu sınıfın kullanıldığı örnek bir web sayfasını projeye dahil edelim. Sayfanın <strong>tasarım zamanındaki(design-time) </strong>görüntüsü ve kodları aşağıdaki gibidir.</p>
<p><img src="/makale/images/mk227_15.gif" alt="" width="453" height="540" border="0" /></p>
<p><em><strong>ProfilYoneticisi.aspx;</strong></em></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProfilYoneticisi.aspx.cs" Inherits="ProfilYoneticisi" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Profil Yönetim Ekranı</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Doğrulama Tipi :<asp:DropDownList ID="ddlDogrulamaTipi" runat="server"></asp:DropDownList>
<asp:Button ID="btnTumProfiller" runat="server" OnClick="btnTumProfiller_Click" Text="Tüm Profilleri Getir" /><br />
Kullanıcı Adı(Benzeri) :
<asp:TextBox ID="txtKullaniciAdi" runat="server"></asp:TextBox>
<asp:Button ID="btnIsmeGoreGetir" runat="server" OnClick="btnIsmeGoreGetir_Click" Text="İsme Göre Getir" /><br />
Aktivite Tarihi :
<br />
<asp:Calendar ID="dtTarih" runat="server"></asp:Calendar>
<br />
<asp:Button ID="btnPasifleriGetir" runat="server" OnClick="btnPasifleriGetir_Click" Text="Pasifleri Getir" /><br />
Bulunan :
<asp:Label ID="lblKullaniciSayisi" runat="server" Text="Label"></asp:Label><br />
<br />
<asp:GridView ID="grdTumProfiller" runat="server" OnRowCommand="grdTumProfiller_RowCommand" OnRowDeleting="grdTumProfiller_RowDeleting">
<Columns>
<asp:CommandField ButtonType="Button" DeleteText="Sil" ShowCancelButton="False" ShowDeleteButton="True" />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html></pre>
<p><em><strong>ProfilYoneticisi.aspx.cs;</strong></em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Profile;
public partial class ProfilYoneticisi : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// ProfileAuthenticationOption enum sabitinin içeriğini Enum sınıfın GetNames metodu ile alarak DropDownList kontrolüne dolduruyoruz.
ddlDogrulamaTipi.DataSource = Enum.GetNames(typeof(ProfileAuthenticationOption));
ddlDogrulamaTipi.DataBind();
}
}
// Bu metod var olan tüm profil bilgilerini getirmek üzere tasarlanmıştır
private void TumProfilleriGetir()
{
// DropDownList kontrolünden seçilen doğrulama kriterinin enum sabitindeki karşılığı seçiliyor.
ProfileAuthenticationOption dogrulamaKriteri=(ProfileAuthenticationOption)Enum.Parse(typeof(ProfileAuthenticationOption), ddlDogrulamaTipi.SelectedValue);
// GetAllProfiles metodu ile doğrulama kriterine uygun olan profil bilgileri ProfileInfoCollection tipinden bir koleksiyona aktarılır.
ProfileInfoCollection tumProfiller = ProfileManager.GetAllProfiles(dogrulamaKriteri);
grdTumProfiller.DataSource = tumProfiller;
grdTumProfiller.DataBind();
// Söz konusu doğrulama kriterine uyan profillerin sayısı elde edilir.
lblKullaniciSayisi.Text = ProfileManager.GetNumberOfProfiles(dogrulamaKriteri).ToString();
}
// GridView kontrolünde Delete işlemi gerçekleştirildiğinde çalışan olay metodudur.
protected void grdTumProfiller_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
int rowIndex=Convert.ToInt32(e.CommandArgument);
string userName=grdTumProfiller.Rows[rowIndex].Cells[1].Text;
// DeleteProfile metodu ile seçilen satırdaki UserName bilgisine ait Profil silinir.
ProfileManager.DeleteProfile(userName);
TumProfilleriGetir();
}
}
protected void btnTumProfiller_Click(object sender, EventArgs e)
{
TumProfilleriGetir();
}
// TextBox içerisine girilen kullanıcı adına benzer olanların Profil bilgilerini getirir.
protected void btnIsmeGoreGetir_Click(object sender, EventArgs e)
{
ProfileAuthenticationOption dogrulamaKriteri = (ProfileAuthenticationOption)Enum.Parse(typeof(ProfileAuthenticationOption), ddlDogrulamaTipi.SelectedValue);
// % kullanılması sayesinde içerisinde TextBox' taki bilgi geçen kullanıcı adlarını elde ederiz.
ProfileInfoCollection profiller = ProfileManager.FindProfilesByUserName(dogrulamaKriteri, "%" + txtKullaniciAdi.Text + "%");
grdTumProfiller.DataSource = profiller;
grdTumProfiller.DataBind();
lblKullaniciSayisi.Text = profiller.Count.ToString();
}
protected void grdTumProfiller_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
}
/* aspnet_Users tablosunda LastActivityDate değeri, seçilen tarihten daha önce olanların elde edilmesini sağlar. Bunun için GetAllInactiveProfiles metodunun ikinci parametresi kullanılır. */
protected void btnPasifleriGetir_Click(object sender, EventArgs e)
{
ProfileAuthenticationOption dogrulamaKriteri = (ProfileAuthenticationOption)Enum.Parse(typeof(ProfileAuthenticationOption), ddlDogrulamaTipi.SelectedValue);
grdTumProfiller.DataSource=ProfileManager.GetAllInactiveProfiles(dogrulamaKriteri, dtTarih.SelectedDate);
grdTumProfiller.DataBind();
lblKullaniciSayisi.Text=ProfileManager.GetNumberOfInactiveProfiles(dogrulamaKriteri, dtTarih.SelectedDate).ToString();
}
}</pre>
<p>Söz konusu örnekte profil yönetimi adına basit işlemler yapılmaktadır. Örnek olması açısında var olan tüm profillerin elde edilmesi, seçilen profillerden herhangibirinin silinmesi, içerisinde belirtilen ada benzer olan profillerin çekilmesi veya belirli bir tarihten öncesine kadar aktivitesi olmayan profillerin getirilmesi gibi işlemler yapılmaktadır. Örneğin tüm profillerin listelenmesi istendiğinde aşağıdakine benzer bir ekran görüntüsü ile karşılaşılır.</p>
<p><img src="/makale/images/mk227_16.gif" alt="" width="568" height="750" border="0" /></p>
<p>Kullanıcı adı benzer olanların listelenmesine örnek olarak aşağıdaki ekran görüntüsünde olduğu gibi içerisinde ma kelimesi geçenler listelenebilir.</p>
<p><img src="/makale/images/mk227_17.gif" alt="" width="527" height="546" border="0" /></p>
<p>Belirli bir süreden öncesine kadar pasif olan kullanıcıların profil listesini elde etmek istediğimizde ise <strong>aspnet_User</strong> tablosunda yer alan <strong>LastActivityDate</strong> değeri baz alınır. Bir başka deyişle aktivite durumuna göre hareket eden <strong> ProfileManager</strong> sınıfı metodları tarih parametresinin değerlerini, söz konusu tablodaki alan ile kıyaslayarak çalışmaktadır. Bu tabloya ait örnek bir ekran görüntüsü aşağıda görüldüğü gibidir.</p>
<p><img src="/makale/images/mk227_13.gif" alt="" width="590" height="253" border="0" /></p>
<p>Çalışma zamanında örneğin 16.09.2007 tarihi ve öncesindeki profil bilgileri elde edilmek istendiğinde aşağıdakine benzer bir ekran görüntüsü ile karşılaşırız.</p>
<p><img src="/makale/images/mk227_14.gif" alt="" width="555" height="605" border="0" /></p>
<p><strong>İsimsiz(Anonymous) kullanıcılar için profil bilgilerini kullanabilmek;</strong></p>
<p>Bazı durumlarda siteyi ziyaret eden<strong> isimsiz kullanıcılar(anonymous users)</strong> içinde profil bilgilerinin tutulması ve saklanması istenebilir. <strong>Profile API</strong>, isimsiz kullanıcılar için oldukça güçlü bir hizmet sağlamaktadır. Sistemin çalışması aslında son derece basittir. Gerekli konfigurasyon ayarlarının yapılmasının ardından siteye bağlanan isimsiz kullanıcılar için birer <strong> GUID</strong> üretilmektedir. Bu <strong>GUID(Global Unique IDentifier)</strong> değerleri bir <strong>çerez(cookie)</strong> yardımıyla istemcinin bilgisayarında saklanırlar. Böylece isimsiz kullanıcıların sunucu tarafında tespit edilebilmesi kolay bir şekilde sağlanmaktadır. Dikkat edilmesi gereken durumlardan birisi istemcilerin <strong>oturum(session)</strong> açışları arasında farklı tarayıcılar kullanabilecek olmasıdır. Bu durumu ilerleyen kısımlarda analiz etmeye çalışacağız. İsimsiz kullanıcılara destek sağlayabilmek için web.config dosyası içerisinde <strong> anonymousIdentification</strong> elementinin ilgili özelliklerinin değerlerinin atanması gerekmektedir. Bir önceki örnek ile karışmaması açısından bu kez yeni bir web uygulaması ile devam edelim. Bu seferki örnekte profil özelliklerini <strong>web.config</strong> dosyası içerisinde tanımlıyor olacağız. Bu amaçla web.config dosyasının içeriğini aşağıdaki gibi tasarladığımızı varsayalım.</p>
<p><strong><em>W</em></strong><em><strong>eb.config;</strong></em></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<compilation debug="true"/>
<authentication mode="Forms"/>
<authorization>
<allow users="*"/>
</authorization>
<anonymousIdentification enabled="true" cookieSlidingExpiration="true" cookieTimeout="3"/>
<profile enabled="true">
<properties>
<add name="SonGirisZamani" type="System.DateTime" allowAnonymous="true"/>
<group name="Urun">
<add name="UrunAdi" type="System.String" allowAnonymous="true"/>
<add name="UrunFiyati" type="System.Double" allowAnonymous="true"/>
</group>
</properties>
</profile>
</system.web>
</configuration></pre>
<p><strong>anonymousIdentification</strong> elementi içerisinde tanımlanabilecek pek çok nitelik(attribute) yer almaktadır. Bu niteliklerden bazıları yukarıdaki örnekte kullanılmıştır. <strong> cookieSlidingExpiration</strong> niteliğine <strong>true</strong> değeri atandığı için istemciler, timeout süresi içerisinde talepte bulundukça çerezlerin(cookies) istemci bilgisayarda kalma süresi uzamaya devam edecektir. <em>(Bu tipik olarak Caching mimarisinde kullanılan Sliding Expiration çalışma sistemi ile aynıdır.)</em> Bununla birlikte <strong> cookieTimeout</strong> niteliğine atanan 3 değeri ile çerezin istemci bilgisayarda 3 dakika süreyle saklanacağı belirtilmektedir. <em> (Uygulamanın <strong>doğrulanmış kullanıcılar(authenticated users) </strong>ile birlikte <strong>isimsiz kullanıcılarada(anonymous users)</strong> hizmet vermesi için <strong>authorization</strong> elementi içerisinde bilinçli olaraktan <strong>tüm kullanıcılar(*)</strong> <strong>allow</strong> edilmiştir.)</em></p>
<p>İlk olarak isimsiz kullanıcılara ait profil bilgilerinin saklandığını ispat etmeye çalışalım. Bu amaçla default.aspx sayfasının içeriğini ve kodlarını aşağıdaki gibi tasarladığımızı düşünelim.</p>
<p><img src="/makale/images/mk227_18.gif" alt="" width="254" height="229" border="0" /></p>
<p><em><strong>Default.aspx;</strong></em></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:LoginName ID="LoginName1" runat="server" />
<asp:LoginStatus ID="LoginStatus1" runat="server" />
<br />
<br />
Son Giriş Zamanı :<asp:Label ID="lblSonGirisZamani" runat="server" Text="Label"></asp:Label><br />
<br />
Urun Adı :<asp:TextBox ID="txtUrunAdi" runat="server"></asp:TextBox><br />
<br />
Urun Fiyatı : <asp:TextBox ID="txtUrunFiyati" runat="server"></asp:TextBox>
<br />
<br />
<asp:Button ID="btnProfilGetir" runat="server" OnClick="btnProfilGetir_Click" Text="Profil Getir" />
<asp:Button ID="btnProfilKaydet" runat="server" OnClick="btnProfilKaydet_Click" Text="Profil Kaydet" /></div>
</form>
</body>
</html></pre>
<p><em><strong>Default.aspx.cs;</strong></em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
protected void btnProfilGetir_Click(object sender, EventArgs e)
{
try
{
lblSonGirisZamani.Text = Profile.SonGirisZamani.ToString();
txtUrunAdi.Text = Profile.Urun.UrunAdi;
txtUrunFiyati.Text = Profile.Urun.UrunFiyati.ToString("C2");
}
catch (Exception exp)
{
Response.Write(exp);
}
}
protected void btnProfilKaydet_Click(object sender, EventArgs e)
{
Profile.SonGirisZamani = DateTime.Now;
Profile.Urun.UrunAdi = txtUrunAdi.Text;
double urunFiyati = 1;
Double.TryParse(txtUrunFiyati.Text, out urunFiyati);
Profile.Urun.UrunFiyati = urunFiyati;
Profile.Save();
}
}</pre>
<p>Uygulamayı çalıştırdığımızda isimsiz bir kullanıcı ile default.aspx sayfasını açabildiğimizi görürüz. Bu aşamada örnek bir kaç veri girip Profil Kaydet başlıklı düğmeye bastığımızda ise bilgilerin başarılı bir şekilde kaydedildiğini görebiliriz. Örneğin aşağıdaki veriler girildiğinde veritabanındaki <strong> aspnet_profile</strong> tablosunada bir satır eklendiği görülecektir.</p>
<p><img src="/makale/images/mk227_19.gif" alt="" width="407" height="307" border="0" /></p>
<p><img src="/makale/images/mk227_20.gif" alt="" width="537" height="65" border="0" /></p>
<p>Dahası kullanıcı siteye 3 dakikalık süre zarfı içerisinde yeni bir <strong>talepte(request) </strong>bulunduğunda ve Profil Getir başlıklı düğmeye bastığında ilgili bilgileri elde edebiliecektir.</p>
<p><img src="/makale/images/mk227_21.gif" alt="" width="401" height="306" border="0" /></p>
<p>Çok doğal olaraktan timeout süresi sona erdikten sonra profil bilgileri getirilmek istenirse aşağıdaki gibi bir ekran görüntüsü alınacaktır.</p>
<p><img src="/makale/images/mk227_22.gif" alt="" width="400" height="314" border="0" /></p>
<p>Bir başka deyişle istemci bilgisayardaki<strong> çerez(cookie)</strong> silindiğinden sunucuda eşleşen bir <strong>GUID</strong> bulunamamaktadır. <em>(GUID' in bulunamaması isimsiz kullanıcının aspnet_users tablosundan silindiği anlamına gelmemektedir)</em> Buda istenen bilgilerin elde edilemeyeceği anlamına gelir. Elbetteki veriler <strong>aspnet_profile</strong> tablosunda durmaya devam edecektir. Bu noktada <strong>ProfileManager</strong> sınıfının ilgili metodlarından yararlanarak belirli periyodlarda söz konusu verilerin temizlenmesi sağlanabilir.</p>
<p>Gelelim kullanıcının aynı uygulamaya isimsiz olarak farklı tarayıcılar ile erişmesi durumuna. Bu amaçla örneği önce <strong>Internet Exlplorer</strong> ile açıp profil bilgilerini kaydediyor olacağız. Sonrasında ise 3 dakikalık zaman dilimi sona ermeden <strong>Firefox</strong> ile aynı sayfayı yeniden talep edeceğiz. Örnek olarak ürün adını Vazo, ürün fiyatını 4.5 olarak girdiğimizi düşünelim.</p>
<p><img src="/makale/images/mk227_23.gif" alt="" width="527" height="662" border="0" /></p>
<p>Dikkat edileceği üzere <strong>Internet Explorer</strong> kullanılıp kaydedilen profil bilgilerine 3 dakikalık süre zarfı içerisinde başka bir tarayıcı program olan <strong>Firefox Mozilla</strong> içerisinde erişilememiştir. Tersi durumda söz konusudur. Buna göre farklı tarayıcı pencereleri ile gelen taleplerde(request) sunucunun farklı bir <strong>GUID</strong> üreteceğini ve istemci bilgisayara çerez olarak yazacağını göz önüne almalıyız.</p>
<p><strong>İsimsiz kullanıcılar(anonymous users)</strong> ile ilgili bir diğer durumda var olan doğrulanmış bir kullanıcı ile birleştirilmeleridir<strong>(Migration)</strong>. Bu durumu daha kolay anlayabilmek için <strong><a href="http://www.amazon.com">amazon.com</a></strong> sitesinin işleyiş şeklini göz önüne alabiliriz. Bu siteye giren kayıtlı bir kullanıcı login olmadan sepete ürünler ekleyebilmektedir. Diğer taraftan kullanıcı alışveriş safhasına geçip sayfaya Login olduğunda, isimsiz kullanıcı olarak sepete attığı bilgiler var olan kullanıcı hesabındaki sepet bilgilerine eklenebilmektedir. Asp.Net 2.0 mimarisinde bu tarz bir işlemi belirli bir ölçüde kontrollü olarak gerçekleştirebilmek için için <strong>Global.asax.cs </strong>dosyasında <strong> Profile_OnMigrateAnonymous</strong> olay metodunun yüklenmesi yeterlidir. Bu metodun aldığı <strong>ProfileMigrateEventArgs</strong> tipinden parametre sayesinde isimsiz kullanıcı(anonymous user) için üretilen <strong>GUID</strong> değerine erişilebilir ve ilgili değerlerin alınarak, login olan kullanıcıya aktarılması sağlanabilir. Yanlız bu noktada daha öncede login olup profil bilgisi kaydedilmiş bir kullanıcının bilgileri üzerine yazılmamaya çalışılmasına özen gösterilmelidir. Bu durumu analiz edebilmek için <strong>global.asax.cs</strong> dosyasına aşağıdaki kod parçasını eklediğimizi göz önüne alabiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false"><%@ Application Language="C#" %>
<script runat="server">
void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs e)
{
/* Önce isimsiz kullanıcının profile bilgisi elde ediliri. GetProfile metodunun dönüş değeri ProfileCommon tipinden olacağı için buna göre bir atama yapılır. */
ProfileCommon isimsizProfil = Profile.GetProfile(e.AnonymousID);
/* Login olan doğrulanmış kullanıcının halen aktiv olup olmadığı öğrenilir. Eğer aktiv ise profil bilgileri mevcut demektir ve üstüne yazılması istenmez. Bu nedenle LastActivityDate değerine bakılarak bir kontrol yapılması gerekmektedir. */
if (Profile.LastActivityDate.Date == DateTime.MinValue.Date)
{
// İsimsiz kullanıcının profil özelliklerinin değerleri ile login olan doğrulanmış kullanıcının profil özellikleri eşleştirilir
Profile.SonGirisZamani = isimsizProfil.SonGirisZamani;
Profile.Urun.UrunAdi = isimsizProfil.Urun.UrunAdi;
Profile.Urun.UrunFiyati = isimsizProfil.Urun.UrunFiyati;
Profile.Save(); // Login olan kullanıcı için değiştirilen profil değerleri kaydedilir.
}
AnonymousIdentificationModule.ClearAnonymousIdentifier();// isimsiz kullanıcı için üretilen çerezin silinmesi sağlanırki bu olay tekrar tetiklenmesin
ProfileManager.DeleteProfile(e.AnonymousID); //isimsiz kullanıcıya ait profil değerleri veritabanından silinir.
}
</script></pre>
<p>Yukarıdaki örnek kod parçasında öncelikli olarak isimsiz kullanıcının profil bilgileri elde edilmektedir. Bu amaçla <strong>GetProfile</strong> metodu kullanılmıştır. Sonrasında ise, o an Login olmuş kullanıcının bir profil bilgisi olup olmadığı tespit edilir. Bu kontrolde <strong>LastActivityDate</strong> özelliğinden yararlanılır. Burada amaç isimsiz kullanıcının(anonymous users) profil bilgilerini, login olmuş kullanıcının var olan profil bilgileri üzerine yazmamaktır. <em>(Burada global bir kontrol yapılıp istemciden bilgilerin üstüne yazmak isteyip istemeyeceği ele alınaraktanda ilerlenebilir. </em></p>
<p><em>Bu tamamen uygulamanın veya sürecin ihtiyaçları doğrultusunda verilebilecek bir karardır.)</em> Sonrasında login olan kullanıcının profile kaydettiği bir bilgi yoksa isimsiz kullanıcı için oluşturulan değerlerin ataması yapılır ve <strong>kaydetme işlemi(save)</strong> gerçekleştirilir. Son olarak olay metodunun tekrardan tetiklenmemesi amacıyla isimsiz kullanıcıya ait bilgi veritabanından silinir. Ayrıca istemcideki çerez bilgiside <strong> AnonymousIdentificationModule</strong> sınıfının <strong>static</strong> <strong> ClearAnonymousIdentifier</strong> metodu ile geçersiz kılınır. Sonuç olarak isimsiz kullanıcı giriş yapıp profil bilgisini kaydettikten sonra daha önceden profil bilgisi bulunmayan bir kullanıcı gibi Login olursa, var olan bilgileri doğrulanmış kullanıcınınkine aktarılacaktır.</p>
<p>Böylece geldik uzun bir makalemizin daha sonuna. Bu makalemizde profile yönetimini biraz daha güçlü ve esnek hale getirmek için neler yapabileceğimizi incelemeye çalıştık. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/makale/images/ProfileManagement.rar">Örnek Uygulama için Tıklayın</a> <em>(Dosya boyutunun küçük olması amacıyla Aspnetdb veritabanları ve log dosyaları çıkartılmıştır)</em></p>2007-10-17T12:00:00+00:00asp.net 2.0profilemanagementbsenyurtUzun süre önce Asp.Net 2.0 ile geliştirilen web uygulamalarında Profile API' sinin nasıl kullanıldığını kısa bir makale üzerinden incelemeye çalışmıştık. Geçtiğimiz günlerde Asp.Net 2.0 ile ilgili bilgilerimi tazelerken profil yönetiminin daha etkin bir şekilde nasıl kullanılabileceğine dair pek çok örnek ile karşılaştım...https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=fb8affde-113f-47a7-bca3-3e5544b3ee0c0https://buraksenyurt.com/trackback.axd?id=fb8affde-113f-47a7-bca3-3e5544b3ee0chttps://buraksenyurt.com/post/Daha-Etkili-Profil(Profile)-Yonetimi-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=fb8affde-113f-47a7-bca3-3e5544b3ee0chttps://buraksenyurt.com/post/Kendi-Web-Part-Bilesenlerimizi-Gelistirmek-2-bsenyurt-com-danKendi Web Part Bilesenlerimizi Gelistirmek - 22007-04-20T12:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Kendi web partlarımızı nasıl geliştirebileceiğimizi ve bu sayede kişiselleştirilebilir web sunucu kontrollerini nasıl yazabileceğimizi bu konu ile ilgili bir önceki makalemizde incelemeye çalışmıştık. Bu makalemizde ise kendi Web Part bileşenlerimize özel fiillerin (Web Part Verbs) nasıl eklenebileceğini ve söz konusu fillerin ne şekilde ele alınabileceğini incelemeye çalışacağız. Web Part kontrollerini herhangibir <strong>WebPartZone</strong> altında kullandığımızda standart olarak bazı <strong>fiilere(Verbs) </strong>sahip oluruz. Tahmin edeceğiniz gibi bu değerler aslında <strong>WebPartManager</strong> tarafından ele alınmakta ve sayfa üzerinde uygun olan web part alanlarının (Web Part Zone) gösterilmesini sağlamaktadır.</p>
<p>Söz gelimi kullanıcı <strong>Edit</strong> isimli fiili(Verb) seçtiğinde <strong>WebPartManager</strong> bileşeni, <strong>EditorZone</strong> kontrolünü aktif hale getirmekte ve söz konusu Web Part kontrolünün değiştirilebilen yada düzenlenebilen özelliklerinin(Properties) bulunduğu bir bileşeni (örneğin <strong>PropertyGridEditorPart</strong> kontrolü) göstermektedir. Bu açıdan bakıldığında fiillerin (Verbs) varsayılan olarak WebPartManager bileşenine ait <strong>Display Mode</strong> değerleri ile yakın bir ilişkide olduklarını söyleyebiliriz. Elbette farklı şekilde davranabilen fiillerde(Verbs) vardır. Örneğin <strong>Minimize</strong> fiili, bulunduğu Web Part' ın içerisinde yer aldığı WebPartZone alanının küçülmesini sağlarken, <strong>Restore</strong> fiili tekrardan eski haline getirilmesine olanak vermektedir.</p>
<p>Kendi Web Part bileşenlerimizi geliştirdiğimizde var olan fiillerin(Verbs) bize yetmediği durumlar söz konusu olabilir. Bu sebepten dolayı istersek kendi Web Part fiillerimizi (Web Part Verbs) oluşturabilir ve kod tarafında ele alarak farklı aksiyonların gerçekleştirilmesini sağlayabiliriz. Burada temel dayanak noktası <strong>WebPartVerb</strong> isimli sınıftır(class). Bu sınıf <strong>IStateManager</strong> isimli arayüzü uyarlayan (implement) bir tiptir. Kullanım amacı, Web Part bileşeni için özel bir fiili tanımlamaktır.</p>
<p>Fiile ait isim (Name), açıklama (Description), seçilme durumu(Checked), resmi(ImageUrl) vb gibi bilgileri içerisinde barındıran bir sınıftır. Kullanıcılar bu fiili seçtiklerinde bir aksiyonun gerçekleştirilmesi gerektiği açık bir şekilde ortadadır. Buda doğal olarak bir metodun çalışma zamanında (run time) tetiklenmesi anlamına gelmektedir. İşte bu sebeple tanımlanan fiilerin gerçekleşmesi halinde çalıştırılacak olan metodları işaret eden <strong>WebPartEventHandler</strong> temsilcilerinden (delegates) faydalanılmaktadır. Bu temsilcinin prototipi ise aşağıdaki gibidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public delegate void WebPartEventHandler(object sender, WebPartEventArgs e);</pre>
<p>Dikkat ederseniz <strong>WebPartEventHandler</strong> temsilcisi standart bir olay temsilcisidir. İkinci paramete olay metoduna bazı bilgileri taşımakta olan <strong>WebPartEventArgs</strong> sınıfına ait bir nesne örneğidir. Bu tipte doğal olarak <strong>EventArgs</strong> sınıfından türetilmiştir. İlk parametre ise fiili gerçekleştiren referansın bir başka deyişle WebPartVerb nesne örneğinin taşıyıcısıdır.</p>
<blockquote>
<p>Hatırlayalım ; Temsilciler (Delegates) çalışma zamanında metodların bellek üzerindeki başlangıç adreslerini işaret eden tiplerdir(types). Tanımlandıklarında, işaret edebilecekleri metodun yapısınıda (parametreleri ve dönüş tipi) belirtirler. Olay tabanlı programlamada (<strong>Event based programming</strong>), Asenkron (<strong>Asynchronous</strong>) mimaride yer alan Polling, Callback, WaitHandle gibi modellerde, çok kanallı uygulamalarda (<strong>Multi Thread Applications</strong>) kullanılmaktadırlar</p>
</blockquote>
<p>Bize gereken tiplerin neler olduğunu öğrendik. Peki bunları kendi Web Part kontrolümüzde nasıl ele alacağız. Bunun için WebPart sınıfından türetme yoluyla kendi Web Part kontrol sınıfımıza gelen <strong>Verbs</strong> isimli özelliğin ezilmesi (override) gerekmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public virtual WebPartVerbCollection Verbs { get; }</pre>
<p>Yukarıda prototipi görünen bu özellik, yanlız okunabilir<strong>(read only)</strong> bir özelliktir ve geriye <strong>WebPartVerbCollection</strong> tipinden bir referans döndürmektedir. WebPartVerbCollection sınıfı türlendirilmiş (strongly typed) bir koleksiyonu temsil etmekte ve Web Part bileşenine eklenecek ekstra fiileri taşımaktadır. Dolayısıyla bu özelliğin get bloğu içerisinde istediğimiz fiileri(Verbs) oluşturmamız ve gereken olay metodu yüklemelerini yapmamız gerekecektir.</p>
<p>Bu makalemizde özellikle üzerinde duracağımız konu kendi fiillerimizi nasıl yazacağımızdır. Konuyu daha iyi anlayabilmek için örnek bir senaryo üzerinden hareket edeceğiz ve göze hoş gelecek bir Web Part kontrolü geliştirmeye çalışacağız. Bu sefer bir önceki Web Part bileşenimizden farklı olarak, <strong>Render</strong> metodu yerine <strong>CreateChildControls</strong> metodunu ezip, bileşen içindeki kontrollerin daha kolay bir şekilde nasıl oluşturulabileceğini de göreceğiz. Dilerseniz amacımızdan bahsederek örneğimizi geliştirelim. Sitemizde çeşitli kategorilerde duvar kağıtları(Wallpapers) olduğunu düşünelim. Siteye giren kullanıcılar seçtikleri kategorideki duvar kağıtlarından kaç tane istiyorlarsa görebilecekler ve istediklerine tıkladıklarında büyük versiyonlarına bakıp bilgisayarlarına indirebilecekler.</p>
<p>Bu senaryda Web Part kullanacağımız için resim sayısı ve kategori gibi bilgileri kişselleşetirme(<strong>Personalization</strong>) şansınada sahip olacağız. Web Part kontrolümüz, seçilen kriterlere göre, kontrolün sayfaya her çizilişinde rastgele resimler seçecek ve bunları gösterecektir. Peki kendi fiillerimizi bu senaryo içerisine nasıl katabiliriz? Kullanıcıların isterlerse Web Part kontrolüne eklenen resimleri yatay veya dikey düzende görebileceklerini göz önüne alalım. Bunun için Yatay Diz ve Dikey Diz başlıklı örnek fiilleri Web Part kontrolümüze ekleyip, resimlerin sayfa üzerindeki diziliş yönlerini belirleyebiliriz. Üstelik bu fiillerin değerlerini kişiselleştirirsek, sayfayı son bıraktığımız haliyle elde edebiliriz. Örnek Web Part kontrolümüzü bitirdiğimizde aşağıdaki ekran görüntülerindekine benzer sonuçlar elde edeceğiz.</p>
<p><strong>Örnek olarak Uçak kategorisinde her ziyaretimizde rastgele 3 resmin yanyana gösterilmesi;</strong></p>
<p><img src="/makale/images/mk200_1.gif" alt="" width="454" height="184" border="0" /></p>
<p><strong>Web Part kontrolümüz için geliştireceğimiz fiiller(Verbs);</strong></p>
<p><img src="/makale/images/mk200_2.gif" alt="" width="507" height="187" border="0" /></p>
<p><strong>Dikey Diz başlıklı Verb seçildiğindeki durum;</strong></p>
<p><img src="/makale/images/mk200_3.gif" alt="" width="170" height="361" border="0" /></p>
<p>Artık kontrolümüzü geliştrimeye başlayabiliriz. Web Part bileşenimizi yine bir <strong>Web Control Library </strong>kütüphanesinde ele alabiliriz. ResimPart adlı Web Part sınıfımızın WebPart sınıfından türemesi(Inherit) gerektiğini hatırlayalım. Kontrolümüz kendi içerisinde <strong>kişiselleştirilebilir (Personalizable)</strong> 3 özellik barındırmalıdır. Bunlardan birisi ziyaretçinin görmek istediği resim sayısını tutan GosterilecekResimSayisi özelliğidir.</p>
<p>Ziyaretçinin görmek istediği kategorinin bilgisini ise ResimKategori isimli özellik ile tutabiliriz. Son olarak ziyaretçinin seçtiği fiile (Verb) uygun olacak şekilde bir değişkenin de kişiselleştirilmesi önemlidir ki bir sonraki ziyarette son bıraktığımız haliyle bir dizilim elde edebilelim. Bu amaçlada ziyaretçinin son seçtiği fiili kişiselleştirilebilir bir özellik olacak şekilde CizimYonu ismiyle saklayacağız. Dilerseniz bahsetmiş olduğumuz özellikleri(Property) aşağıdaki gibi yazarak makalemize devam edelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">[ToolboxData("<{0}:ResimPart runat=server></{0}:ResimPart>")]
public class ResimPart:WebPart
{
#region Kişiselleştirilebilir özellikler için alan tanımlamaları
private ResimKategorisi _kategori;
private int _gosterilecekResimSayisi;
private Yon _cizimYonu;
#endregion
#region Kişiselleştirilebilir Özellikler
[WebBrowsable(true)]
[WebDescription("Bakmak istediğimiz resimlerin kategorisi")]
[WebDisplayName("Resim Kategorisi")]
[Personalizable(PersonalizationScope.User, false)]
public ResimKategorisi Kategori
{
get { return _kategori; }
set { _kategori = value; }
}
[WebBrowsable(true)]
[WebDescription("Seçilen kategoride gösterilecek resim sayısı")]
[WebDisplayName("Resim Sayısı")]
[Personalizable(PersonalizationScope.User, false)]
public int GosterilecekResimSayisi
{
get{return _gosterilecekResimSayisi <= 0 ? 1 : _gosterilecekResimSayisi;}
set{_gosterilecekResimSayisi = value <= 0 ? 1 : value;}
}
[WebBrowsable(true)]
[WebDisplayName("Resimlerin Yönü")]
[WebDescription("Resimler dikey veya yatay gösterilebilmesini sağlar")]
[Personalizable(PersonalizationScope.User, false)]
public Yon CizimYonu
{
get{return _cizimYonu;}
set{_cizimYonu = value;}
}
#endregion
}</pre>
<p>Bir önceki makalemizden de hatırlayacağınız gibi, özelliklerimizi kişiselleştirmek için gerekli nitelikler ile işaretliyoruz. Resimlerin yönünü ve var olan resim kategorilerini birer <strong>enum</strong> sabiti içerisinde saklamaktayız.</p>
<blockquote>
<p>Enum sabitleri bu senaryoda oldukça işe yarar. Ne varki kategori isimlerinin değiştiği yada yeni kategorilerin eklendiği durumlarda koda girip güncelleme yapmak ve uygulamayı tekrardan build etmek gerekecektir. Alternatif bir yol olarak bu tip bir verinin kod dışında bir ortamda, örneğin bir Xml dosyasında veya veritabanındaki bir parametre tablosunda saklanması göz önüne alınabilir.</p>
</blockquote>
<p>Normal şartlarda resimlerin kategorisi için daha farklı bir yöntem izlemek sağlıklı olacaktır. Kendi sistemimizde, aşağıdaki ekran görüntüsünde yer alan klasör yapısını baz alacak bir enum sabiti ele alınmaktadır. Resimlerin bir küçük birde orjinal hallerini tutmak için klasörleme mantığını kullanıyoruz. Buna göre söz konusu resimleri kategori adları şeklinde olan klasörler içerisinde yer alan big ve small alt klasörlerinde ayrıştırmış durumdayız. Web Part kontrolümüz küçük boyutlu resimleri small isimli klasörler altından çekerken, üzerlerine tıklandığında orjinal büyüklüklerindeki versiyonları ise boş bir tarayıcı penceresinde açacak şekilde big isimli alt klasörlerden çekmektedir.</p>
<p><img src="/makale/images/mk200_4.gif" alt="" width="235" height="282" border="0" /></p>
<p>Buna göre kullanıcının enum sabiti yardımıyla seçtiği kategorideki resimleri görebilmesi için, klasör adı ile enum sabiti adının aynı olması gerekir. Bu durumda yanlışlıkla klasörün isminin değiştirilmesi sonucu sistem beklediğimiz şekilde çalışmayacaktır. Özellikle istenen kategoriye bağlı klasör bulunamayacağından çalışma zamanı istisnaları (run time exception) alınması kaçınılmazdır. Bu durumun önüne geçmek için neler yapılabileceğini düşünmekte fayda olacağı kanısındayım. Bize yardımcı olacak enum sabitlerimiz aşağıdaki gibidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public enum ResimKategorisi
{
araba,
ucak,
manzara,
komik
}
public enum Yon
{
DikeyYon,
YatayYon
}</pre>
<p>Şimdide Web Part kontrolümüz için gerekli fiillerimizi (Verbs) geliştirelim. Hatırlayacağınız gibi makalemizin başında bu iş için Verbs özelliğini ezmemiz<strong>(override)</strong> gerektiğini söylemiştik. Bu özellik içerisinde tanımlayacağımız <strong>WebPartVerb</strong> tipinden nesne örneklerinin yapıcı metodları(constructors) içerisinde, ilgili fiil(Verb) seçildiği zaman çalıştırılacak olan metodu işaret edecek bir temsilci tanımı yapılmaktadır. Bunları hesaba katarak Web Part kontrolümüzün içeriğini ilk aşamada aşağıdaki gibi geliştirebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">WebPartVerb vrbYatay, vrbDikey;
public override WebPartVerbCollection Verbs
{
get
{
vrbDikey = new WebPartVerb("DikeyDizilim", new WebPartEventHandler(DikeyDiz));
vrbYatay = new WebPartVerb("YatayDizilim", new WebPartEventHandler(YatayDiz));
vrbDikey.Text = "Dikey Diz";
vrbYatay.Text = "Yatay Diz";
WebPartVerb[] verbs = new WebPartVerb[2];
verbs[0] = vrbDikey;
verbs[1] = vrbYatay;
WebPartVerbCollection verbCollection = new WebPartVerbCollection(verbs);
return verbCollection;
}
}
public void YatayDiz(object sender, WebPartEventArgs e)
{
CizimYonu = Yon.YatayYon;
}
public void DikeyDiz(object sender, WebPartEventArgs e)
{
CizimYonu = Yon.DikeyYon;
}</pre>
<p>Şimdi neler yaptığımıza kısaca bakalım. Kendi yazacağımız fiillerimizi WebPartVerb tipinden tanımladıktan sonra <strong>get</strong> bloğu içerisinde oluşturmaktayız. Bu işlemi yaparken ikinci parametre ile bir <strong>WebPartEventHandler</strong> temsilci örneği tanımladığımıza ve DikeyDiz ile YatayDiz isimli metodları işaret ettiğimize dikkat edelim. Buna göre, kullanıcılar bu fiillerden birisine tıkladığında YatayDiz ve DikeyDiz isimli metodlar çalışacaktır. Bu metodların içerisinde Web Part kontrolümüz için tanımladığımız CizimYonu özelliğinin değerini berlilemekteyiz. Oluşturulan <strong>WebPartVerb</strong> kontrollerini bir dizi içerisinde topladıktan sonra bir <strong>WebPartVerbCollection</strong> koleksiyonunun üretilmesinde kullanıyoruz. Son olarak get bloğundan bu koleksiyonu geri döndürmekteyiz. Peki fiilin seçilmesi sonucunda resimleri yatay veya dikey olarak nasıl yerleştireceğiz?</p>
<p>Sonuç itibariyle seçilen resimlerin ekrana alınması ve belirli bir yöne doğru çizilmesi demek, Web Part kontrolünün ekrana çizilmesi sırasında (Render) uygun HTML takılarının(Tag) oluşturulması demektir. Hatırlayacağınız gibi bir önceki makalemizde bu iş için <strong>Render</strong> metodunu kullanmıştık. Render metodu dışında var olan Web sunucu kontrollerinden yararlanaraktanda aynı işlemi gerçekleştirebilmekteyiz. Sonuç itibariyle Web Part kontrolleride birer taşıyıcı (<strong>Container</strong>) olduğundan bir <strong>Controls</strong> koleksiyonuna sahiptir. Dolayısıyla sunucu kontrollerini oluşturup bu koleksiyona ekleyerek HTML elementleri ile fazla uğraşmadan render işlemlerini gerçekleştirebiliriz. Web Part kontrollerinde bu işlem için tek yapmamız gereken <strong>CreateChildControls</strong> metodunu ezmek olacaktır. Kendi örneğimiz için bu metodu aşağıdaki gibi ezebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">protected override void CreateChildControls()
{
string sanalAdres = HttpContext.Current.Request.Url.ToString();
sanalAdres = sanalAdres.Substring(0, sanalAdres.LastIndexOf('/'));
string sanalResimAdresi = sanalAdres + ("/images/") + Kategori.ToString();
string fizikiKlasor = HttpContext.Current.Request.PhysicalPath;
fizikiKlasor = fizikiKlasor.Substring(0, fizikiKlasor.LastIndexOf('\\'));
int siraNo = 0;
try
{
FileInfo[] resimDosyalari = DosyalariAl(fizikiKlasor);
Random rnd = new Random();
Table tablo = new Table();
if (CizimYonu == Yon.DikeyYon)
{
for (int i = 0; i < GosterilecekResimSayisi; i++)
{
TableRow satir = new TableRow();
siraNo = rnd.Next(0, resimDosyalari.Length);
TableCell hucre = HucreOlustur(sanalResimAdresi, siraNo, resimDosyalari);
satir.Cells.Add(hucre);
tablo.Rows.Add(satir);
}
}
else if (CizimYonu == Yon.YatayYon)
{
TableRow satir = new TableRow();
for (int i = 0; i < GosterilecekResimSayisi; i++)
{
siraNo = rnd.Next(0, resimDosyalari.Length);
TableCell hucre=HucreOlustur(sanalResimAdresi, siraNo, resimDosyalari);
satir.Cells.Add(hucre);
}
tablo.Rows.Add(satir);
}
Controls.Add(tablo);
}
catch
{
}
}
private static TableCell HucreOlustur(string sanalResimAdresi, int siraNo, FileInfo[] resimDosyalari)
{
string miniResimDosyaAdresi = sanalResimAdresi + "/small/" + resimDosyalari[siraNo].Name;
string buyukResimDosyaAdresi = sanalResimAdresi + "/big/" + resimDosyalari[siraNo].Name;
TableCell hucre = new TableCell();
string resim = "<a href='" + buyukResimDosyaAdresi + "' target='_blank'><img src='" + miniResimDosyaAdresi + "'/></a>";
hucre.Text = resim;
return hucre;
}
private FileInfo[] DosyalariAl(string fizikiKlasor)
{
DirectoryInfo fizikiKlasorBilgisi = new DirectoryInfo(fizikiKlasor + "\\images\\" + Kategori.ToString() + "\\small\\");
FileInfo[] resimDosyalari = fizikiKlasorBilgisi.GetFiles();
return resimDosyalari;
}</pre>
<p>Burada kendimize göre bir algoritma geliştirdik. Temel olarak <strong>CreateChildControls</strong> metodu seçilen fiile göre yatay veya dikey dizilime uygun olacak şekilde bir HTML Table üretmekte ve <strong>Controls</strong> koleksiyonuna eklemektedir. Burada söz konusu olan <strong>Table</strong>, <strong>TableRow</strong> ve <strong>TableCell</strong> tipleri yönetimli kod(managed code) tarafında geliştirilmiş sunucu bileşenleridir ve çalışma zamanında üretilen sayfa içerisinde HTML Table, HTML TR (Satır), HTML TD (Hücre) elementlerine dönüştürülmektedir.</p>
<p>Özel olarak, resimlerin küçük hallerini göstermek ve üzerlerine tıklandığında orjinal boyutlarında açmak için <strong>a href</strong> ve <strong>img</strong> HTML elementlerinden faydalanılmaktadır. Sizler bu kod parçasını daha efektif hale getirerek (örneğin optimize ederek) daha ölçeklenebilir bir şekle döndürebilirsiniz. Artık geliştirdiğimiz WebPart bileşenini örnek bir web uygulaması üzerinde deneyebiliriz. Bu amaçla geliştireceğimiz web uygulamasının kişiselleştirmeye destek verebilmesi amacıyla <strong>Membership</strong> ayarlarını içermesi doğru olacaktır. Sonuç olarak aşağıdaki Flash animasyonunda görülen çıktıyı elde ederiz. <em>(Flash dosyasının boyutu 180 Kb olup yüklenmesi zaman alabilir)</em></p>
<p><em><strong><span style="font-family: Verdana; color: #ff0000;"> <object id="obj1" width="625" height="569" classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" border="0">
<param name="movie" value="http://www.buraksenyurt.com/makale/images/mk200_Video.swf" />
<param name="quality" value="High" /> <embed type="application/x-shockwave-flash" width="625" height="569" src="http://www.buraksenyurt.com/makale/images/mk200_Video.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" name="obj1" /></object> </span></strong></em></p>
<p>Yukarıdaki Flash animasyonundanda gördüğünüz gibi, sisteme giren bir kullanıcı kendisine göre istediği kategorideki resimleri gösterebilmekte ve bunları yatay veya dikey olarak dizebilmektedir. Bizim dikkat etmemiz gereken nokta kendi fiillerimizin burada işlenebilir olmasıdır. Yukarıda geliştirdiğimiz ResimPart isimli Web Part kontrolümüzde fiillerimizi ayrı olay metodlarına yönlendirdik. Dilersek tüm fiillerimizi aynı metod içerisinde ele alabiliriz. Bunun için öncelikli olarak <strong>WebPartVerb</strong> bileşenlerimizi oluştururken kullanıdığımız <strong>WebPartEventHandler</strong> temsilcilerini (delegates) aynı metodu işaret edecek şekilde oluşturmalıyız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">vrbDikey = new WebPartVerb("DikeyDizilim", new WebPartEventHandler(VerbUygula));
vrbYatay = new WebPartVerb("YatayDizilim", new WebPartEventHandler(VerbUygula));</pre>
<p>Daha sonra VerbUygula isimli metodumuzu aşağıdaki gibi kodlamamız yeterli olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public void VerbUygula(object sender, WebPartEventArgs e)
{
string tetiklenenVerbId=((WebPartVerb)sender).ID;
if (tetiklenenVerbId == "DikeyDizilim")
CizimYonu = Yon.DikeyYon;
else if (tetiklenenVerbId == "YatayDizilim")
CizimYonu = Yon.YatayYon;
}</pre>
<p>Tüm fiiller aynı olay metodu içerisinde ele alınacaklarından, olayı meydana getiren <strong>WebPartVerb</strong> nesne örneğinin kim olduğunun bilinmesi gerekmektedir. Bunun için olay metodunun ilk parametresinden yararlanılmıştır. sender isimli değişken, olay metodu içerisinde <strong>WebPartVerb</strong> tipine dönüştürülmüş ve <strong>ID</strong> özelliğinin değerine bakılarak CizimYonu isimli enum sabitine uygun değeri atanmıştır. Uygulamayı bu haliyle test ettiğimizde ilk versiyondaki ile aynı sonuçları elde ederiz.</p>
<p>Çözmemiz gereken bir durum daha vardır. Bu da hangi Verb seçildiyse bunun başına bir check işaret konulmasının sağlanmasıdır. Bu amaçla WebPartVerb sınıfının <strong>Checked</strong> özelliğinin değerinin true veya false olarak değiştirilmesi yeterlidir. Lakin asıl problem bununla ilişki kodun nereye yazılması gerektiğidir. Web sayfaları ve içerisinde yer alan kontrollerin yaşam döngüsü olayı biraz karmaşık hale getirmektedir. Senaryomuzda, işaretleme operasyonu için gerekli kod parçasının <strong>CreateChildControls</strong> metodu içerisine konulması düşünülebilir. Yada bu işlemi fiilleri ele aldığımız olay metodu içerisine koyabiliriz. Ancak hiç birisi işe yaramayacaktır. Bunun sebebi Web Part bileşenimizin, sunucu tarafındaki yaşam döngüsüdür. Dilerseniz yaşam döngüsünü inceleyerek devam edelim. Sayfa ilk kez talep edildiğinde dolayısıyla Web Part kontrolüde ilk kez oluşturulduğundaki olay işleyiş sırası göz önüne alındığında, Web Part kontrolümüzdeki bazı üyelerin işleyiş sırası aşağıdaki gibi olacaktır.</p>
<p><img src="/makale/images/mk200_5.gif" alt="" width="168" height="167" border="0" /></p>
<p>Kullanıcı bir fiil (Verb) seçtikten sonra sayfa sunucuda tekrar oluşturulup, web part kontrolümüzde yeniden oluşturulacaktır. Bu ikinci request sonrasında üylerin işleyiş sırası ise aşağıdaki gibidir.</p>
<p><img src="/makale/images/mk200_6.gif" alt="" width="168" height="335" border="0" /></p>
<p>Görüldüğü gibi her iki yaşam döngüsü sırasında son olarak <strong>Verbs</strong> isimli özelliğe girilmektedir. Dolayısıyla işaretleme kodlarını buraya dahil edebiliriz. Bununla birlikte göze çarpan bir diğer durum, ikinci talep sonrasında Verb özelliğinin iki kez devreye giriyor olmasıdır. Bu sebepten, Verb özelliğinin <strong>get</strong> bloğundaki kodlar aşağıdaki gibi optimize edilmeli ve <strong>WebPartVerb</strong> bileşenleri ile ilgili koleksiyonun oluşturulma işlemlerinin sadece bir kez yapılması garanti altına alınmalıdır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">WebPartVerb vrbYatay, vrbDikey;
WebPartVerbCollection verbCollection;
public override WebPartVerbCollection Verbs
{
get
{
if (vrbYatay == null && vrbDikey == null)
{
vrbDikey = new WebPartVerb("DikeyDizilim", new WebPartEventHandler(VerbUygula));
vrbYatay = new WebPartVerb("YatayDizilim", new WebPartEventHandler(VerbUygula));
vrbDikey.Text = "Dikey Diz";
vrbYatay.Text = "Yatay Diz";
WebPartVerb[] verbs = new WebPartVerb[2];
verbs[0] = vrbDikey;
verbs[1] = vrbYatay;
verbCollection = new WebPartVerbCollection(verbs);
}
if (CizimYonu == Yon.DikeyYon)
{
vrbDikey.Checked = true;
vrbYatay.Checked = false;
}
else if (CizimYonu == Yon.YatayYon)
{
vrbDikey.Checked = false;
vrbYatay.Checked = true;
}
return verbCollection;
}
}</pre>
<p>Artık çalışma zamanında seçtiğimiz fiillerin yanında tik işaret aşağıdaki resimdekine benzer bir şekilde çıkacaktır.</p>
<p><img src="/makale/images/mk200_7.gif" alt="" width="156" height="158" border="0" /></p>
<p>Böylece geldik bir makalemizin daha sonuna. Bu makalemizde kendi Web Part kontrollerimize özel fiilleri (<strong>WebPartVerb</strong> tipinden nesne örneklerini) nasıl ekleyebileceğimizi örnek bir senaryo üzerinden incelemeye çalıştık. Makalemizin sonlarında kontrollerin yaşam döngüsünün ne kadar önemli olabileceğini gördük. Bir sonraki makalemizde görüşmek dileğiyle hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/makale/images/VerbSample.rar">Örnek Uygulama İçin Tıklayınız.</a> <em>(Resimlerin boyutlarının büyük olması nedeni ile Big isimli kasörlerlerdeki resim dosyaları ve App_Data altındaki ASPNETDb.mdf dosyaları silinmiştir. Test ederken bunları göz önüne almayı unutmayınız.)</em></p>2007-04-20T12:00:00+00:00asp.net 2.0web partscustom web partsbsenyurtKendi web partlarımızı nasıl geliştirebileceiğimizi ve bu sayede kişiselleştirilebilir web sunucu kontrollerini nasıl yazabileceğimizi bu konu ile ilgili bir önceki makalemizde incelemeye çalışmıştık. Bu makalemizde ise kendi Web Part bileşenlerimize özel fiillerin (Web Part Verbs) nasıl eklenebileceğini ve söz konusu fillerin ne şekilde ele alınabileceğini incelemeye çalışacağız.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=e78d3269-73ff-4a65-a901-b9a25c99b7520https://buraksenyurt.com/trackback.axd?id=e78d3269-73ff-4a65-a901-b9a25c99b752https://buraksenyurt.com/post/Kendi-Web-Part-Bilesenlerimizi-Gelistirmek-2-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=e78d3269-73ff-4a65-a901-b9a25c99b752https://buraksenyurt.com/post/Kendi-Web-Part-Bilesenlerimizi-Gelistirmek-bsenyurt-com-danKendi Web Part Bileşenlerimizi Geliştirmek2007-03-28T12:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Web uygulamalarında var olan bileşenlerin yetersiz kaldığı durumlarda kendi kontrollerimizi geliştirme yoluna gidebiliyoruz. Kendi kontrollerimizi geliştirirken seçebileceğimiz yollar bellidir. Var olan bir web bileşeninden türetme yolunu seçebiliriz <strong>(Inherited Controls)</strong>. Bu durumda kontrolün Html çıktısının ne olacağını bir başka deyişle Render işlemlerini çok fazla düşünmemize gerek kalmaz. Tek yapmamız gereken var olan üyeleri ezmek (override) veya yeni üyeler katmaktır. Bir diğer yol birden fazla kontrolü içeren komposit bir bileşen geliştirmektir<strong>(Composite Controls).</strong> Bu tekniğe verilebilecek en güzel örnek kullanıcı web kontrolleridir <strong>(web user controls)</strong>. Özel bileşen geliştirmenin belkide en zor seçeneği, kontrolü sıfırdan yazmaktır. Bu durumda, ilgili bileşenin istemci tarayıcılarındaki Html çıktısını düşünmekle kalmayıp, <strong>ViewState</strong>, <strong>Postback, Event Handling</strong> gibi temel konularında göz önüne alınması ve düşünülmesi gerekir.</p>
<p>Asp.Net 2.0 ile birlikte <strong>WebPart</strong> adı verilen bir Framework gelmiştir. Bu Framework istersek kolay bir şekilde kişiselleştirilebilir <strong>(Personalizable)</strong> web kontrolleri yazabilmemizede olanak sağlamaktadır. Aslında bahsettiğimiz özelleştirilmiş kontrollerin birer <strong>WebPart</strong> tipi olduğunu söylemek gerekir. Dolayısıyla, kendi WebPart bileşenlerimizi türetme yardımıyla kolay bir şekilde geliştirebilir ve kullanabiliriz. WebPart' lar sıfırdan yazılan bileşen kontrolleri ve web kullanıcı kontrollerine nazaran, WebPart Framework için tam destek sağlarlar. Bu da, kişiselleştirmenin kullanıldığı sayfalarda oldukça önemli bir meziyettir. İşte biz bu makalemizde basit olarak kendi Web Part bileşenlerimizi nasıl geliştirebileceğimizi incelemeye çalışacağız.</p>
<p>Normal şartlar altında sayfada yer alan herhangiri <strong>Web Part Zone</strong> içerisine alınan her bileşen otomatik olarak Web Part muamalesi görür. Lakin kendi Web Part bileşenlerimizi geliştirdiğimizde, daha öncedende belirttiğimiz gibi Web Part Framework' ü içerisindeki özelliklerin ve fonksiyonelliklerin tamamını kullanabilme şansına sahip oluruz. Öyleyse işe <strong>WebPart</strong> tipinden türeyen bir sınıf yazmak ile başlamak lazım.</p>
<blockquote>
<p>WebPart tipi <strong>abstract</strong> bir sınıf olup, bir Web Part bileşeni için gerekli tüm temel alt yapıyı sunmaktadır.</p>
</blockquote>
<p>Bildiğiniz gibi, Visual Studio 2005 içerisindeki proje şablonlarından biriside, <strong>Web Control Library</strong> seçeneğidir. Web Control Library, standart olarak örnek bir özel kontrol (Custom Control) sınıfı içerir. Aynı zamanda Web ortamı için gerekli temel referanslarıda hazır olarak barındırır. <em>(Örneğin System.Web)</em> Biz Web Part bileşenimizi böyle bir kontrol kütüphanesi içerisine dahil edersek, herhangibir web uygulamasında kolayca kullanabiliriz. Dahası, geliştirdiğimiz Web Part bileşenlerini <strong>Visual Studio ToolBox</strong> içerisinde ele alabiliriz. Bu da Web Part bileşenimizi bir kontrol olarak ToolBox içerisinde tutabileceğimiz anlamına gelmektedir.</p>
<p>Gelelim makalemizin örnek senaryosuna. Kendi Web Part kontrollerimizi geliştirmeyi öğrenirken, standart olarak ele alınan senaryo, <strong>RSS</strong> bilgilerini tutan bir bileşenin yazılmasıdır. Bizde geleneği bozmayıp bu tip bir Web Part bileşenini nasıl yazabileceğimizi incelemeye çalışacağız. Ancak başlamadan önce RSS ile ilişkili olarak biraz bilgi vermekte fayda olacağı kanısındayım. Günümüzde pek çok web sitesi, güncel olarak yayınlamak istedikleri bilgilerin, başkaları tarafından kolay bir şekilde ele alınabilmesi amacıyla, Xml tabanlı içerikler sunarlar.</p>
<p>RSS bu anlamda Xml verisini standardize etmektedir. Böylece, her RSS içeriğinin aynı şemaya sahip olması sağlanmış olur. Bizde bu yaklaşımı kullanacağız. Özellikle .Net Framework, Xml üzerinde son derece etkili yönetimli tipler<strong> (managed types)</strong> sunmaktadır. Bu tiplerden faydalanarak her hangibir RSS içeriğini kolay bir şekilde ayrıştırabiliriz <strong>(parsing).</strong> Aşağıdaki ekran görüntüsünde C#Nedir? sitesinin <a href="http://www.csharpnedir.com/Rss.xml"> http://www.csharpnedir.com/Rss.xml</a> adresinden yayınlanan RSS dökümanının bir parçasını görmektesiniz.</p>
<p><img src="/makale/images/mk197_1.gif" alt="" width="588" height="389" border="0" /></p>
<p>Dikkat ederseniz, rss isimli root element içerisinde <strong>channel</strong> isimli tek bir alt boğum <strong>(childe node)</strong> vardır. Bu node içerisinde RSS dökümanın sahibi ile ilişkili çeşitli bilgiler yer alır. Örneğin, RSS konusunu anlatan başlık(title) ve açıklama(description) bilgisi, RSS sahibinin web adresi(link) gibi. Diğer taraftan <strong>item</strong> elementleri içerisindede RSS ile yayınlanmak istenen asıl içerik yer alır. Özetle bu RSS dökümanı ile çalışma zamanında C#Nedir? sitesinde yayınlanan son makalelerin listesini elde edebilir, bağlantıları kullanarak buralara geçiş yapabiliriz. Eğer bu RSS dökümanını internet üzerinden talep edersek aşağıdakine benzer bir ekran görüntüsü alırız.</p>
<p><img src="/makale/images/mk197_3.gif" alt="" width="423" height="435" border="0" /></p>
<p>İşte Web Part kontrolümüz, kişi bazında herhangibir RSS bilgisini sunacak şekilde tasarlanacaktır. Burada RSS' in Url bilgisini ve hatta RSS' in sahibi ile ilgili kısa bir bilgiyi, kişi bazında ayrı ayrı saklayabiliriz. Gelin Web Part kontrolümüzü yazarak kişiselleştirilebilir bir RSS okuyucunun nasıl yapılabileceğini görmeye çalışalım. İlk olarak <strong>Web Control Library</strong> projemize bir sınıf ekleyeceğiz. Sınıfımızın WebPart sınıfından türemiş olması gerekmektedir.</p>
<blockquote>
<p>Bir sınıfı WebPart abstract sınıfından türettiğimizde, söz konusu yeni tip, Web Part Framework' ünü kullanabileceğimiz üyelerede sahip olur. Bu üyeler WebPart sınfınında türediği çeşitli tipler içerisinde toplanmaktadır. Aşağıdaki şekilde, geliştirdiğimiz örnek web part bileşeninin nesne hiyerarşisini görebilirsiniz.</p>
</blockquote>
<p><img src="/makale/images/mk197_2.gif" alt="" width="556" height="431" border="0" /></p>
<p>İlk olarak RssPart isimli bir sınıfı WebPart tipinden türeyecek şekilde aşağıdaki gibi tanımlayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">[ToolboxData("<{0}:RssPart runat=server></{0}:RssPart>")]
public class RssPart : WebPart
{
}</pre>
<p>Geliştirdiğimiz sınıf her ne kadar bir Web Part bileşeni olsada, sunucu taraflı <strong>(server side)</strong> bir kontroldür. Bu nedenle, aspx dosyalarının kaynak kısmında birer takı<strong>(tag)</strong> içerisinde ele alınacaktır. <strong>ToolboxData</strong> isimli niteliğin eklenmesinin sebebi de budur. Buna göre Web Part bileşenimizi herhangibir aspx sayfasına eklediğimizde aşağıdakine benzer bir element içeriği ile karşılaşırız.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><cc1:RssPart ID="RssPart1" runat="server"/></pre>
<p>Burada cc1 ön ekinin (prefix) nereden geldiğini merak ediyor olabilirsiniz. Aslında ToolBox' tan bir WebPart kontrolünü sayfaya sürükleyip bıraktığımızda otomatik olarak sayfanın başına <strong>Register</strong> direktifi eklenecektir. <em>(Bu ilk kontrol sürüklenip bırakıldığında otomatik olarak oluşur) </em>Bu direktif temel olarak, Asp.Net uygulamasında kullanılacak olan Web Part bileşenini içeren <strong>sınıf kütüphanesinin (Contol Library</strong>) referans bilgisinide içerecektir. Kısaca Register direktifimizinde aşağıdaki gibi olacağını söyleyebiliriz.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Register Assembly="MyWebParts" Namespace="MyWebParts" TagPrefix="cc1" %></pre>
<p>Gelelim Web Part kontrolümüz içerisindeki üyelere. Kontrolümüzün RSS belgesine ait Url bilgisini ve kısa bir başlık bilgisini tutacağını varsayabiliriz. Bu bilgiler için yapacağımız normal şartlar altında birer özellik yazmak olacaktır. Lakin bu sefer özelliklerimizin, kişi bazında özelleştirilebilmesini istiyoruz. Bu nedenle <strong>Personalizable</strong> niteliğini kullanmamız gerekiyor.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">private string _Url;
private string _RssOwner;
[WebBrowsable(true)] // PropertyGridEditorPart içerisinde bu özelliğin gösterilip gösterilmeyeceğini belirtir.
[WebDescription("Verilen Url adresine göre Rss bilgisini okur")]
[Personalizable(PersonalizationScope.User)] // Özelliğin değerinin Membership tablolarında kullanıcı bazında tutulacağını belirtir.
[WebDisplayName("Rss Bilgisi Alınacak Url")] // PropertyGridEditorPart içerisinde Url özelliği için gösterilecek bilgi
public string Url
{
get { return _Url; }
set { _Url = value; }
}
[WebBrowsable(true)]
[WebDescription("Rss sahibine ait bilgiyi içerir")]
[Personalizable( PersonalizationScope.User)]
[WebDisplayName("Rss Yayımcısı")]
public string RssOwner
{
set { _RssOwner = value; }
get{return _RssOwner;}
}</pre>
<p>Url ve RssOwner isimli özelliklerimiz, kişiselleştirilebilir üyelerdir. <strong>Personalizable</strong> niteliğine atanan <strong>PersonalizableScop.User</strong> değeri sayesinde Url ve RssOwner özelliklerinin işaret ettikleri değerlerin, kişi bazında Membership API' si üzerinden ilgili tablolarda tutulabileceği belirtilmektedir. <strong> WebBrowsable</strong> niteliği ile, özelliğin bir <strong>PropertyGridEditorPart</strong> bileşeni içerisinde gösterilip gösterilmeyeceğine karar verilebilir. Buna göre Url ve RssOwner isimli özelliklerimizi, sayfayı ziyaret eden kullanıcılar isterlerse <strong>PropertyGridEditorPart</strong> içerisinden değiştirebilirler. <strong>WebDescription</strong> niteliği(attribute), PropertyGridEditorPart içerisinde gösterilecek olan özelliklerin üzerlerine gelindiğinde kısa açıklama kutucukları gösterilmesini sağlar. Son olarak <strong>WebDisplayName</strong> niteliği sayesinde, özelliklerin ProperyGridEditorPart içerisinde hangi adlar ile sunulacağı belirtilmektedir.</p>
<p>Kişi bazında tutulacak özelliklerimizide belirttikten sonra, Web Part kontrolümüzün ekrana nasıl çizileceğini ayarlayabiliriz. Bildiğiniz gibi, her sunucu web bileşeni istemci tarafında Html çıktıları haline getirilirler (Render işlemi). Burada da, belirtilen Url adresindeki RSS dökümanını ayrıştırdıktan(parsing) sonra örnek olarak linkler halinde göstermek isteyebiliriz. Bu çıktının düzenli bir formatta olmasını sağlamak içinde Html tablolarına başvurabiliriz. Bir sunucu kontrolünde, Html çıktısını oluşturmak için Render metodu göz önüne alınabilir. Diğer taratfan <strong> CreateChildControls</strong> metoduna başvurup daha kolay bir biçimde çıktı üretebiliriz. CreateChildControls metodunu ilerleyen makalelerimizde ele almaya çalışacağız. Şimdi dilerseniz, Web Part bileşenimiz için <strong> Render</strong> metodunu ezelim<strong> (override)</strong> ve Html çıktısını, RSS içeriğine göre hazırlayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">protected override void Render(HtmlTextWriter output)
{
if (!String.IsNullOrEmpty(Url))
{
try
{
XmlReader reader = XmlReader.Create(Url);
DataSet ds = new DataSet();
ds.ReadXml(reader);
DataTable items = ds.Tables["item"];
#region Render Table
// Table elementi render edilmeden önce gerekli style attribute' ları ekleniyor
output.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "WhiteSmoke"); // Arka plan rengi
output.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%"); // genişlik belirleniyor
output.RenderBeginTag(HtmlTextWriterTag.Table); // Table için açılış takısı
output.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "Gold");
output.RenderBeginTag(HtmlTextWriterTag.Tr); // Tr açılış takısı (satır)
output.RenderBeginTag(HtmlTextWriterTag.Td); // Td açılış takısı (hücre)
output.Write(ds.Tables["channel"].Rows[0]["title"].ToString()); // Td içerisine Rss dökümanından title bilgisi alınıyor
output.RenderEndTag(); // Td için kapanış takısı
output.RenderEndTag(); // Tr için kapanış takısı
// Rss dökümanındaki her bir item için bir Tr (Table Row) ve içerisinde bir Td (hücre) oluşturuluyor
for (int i = 0; i < items.Rows.Count; i++)
{
output.RenderBeginTag(HtmlTextWriterTag.Tr); // Tr açılış takısı
output.RenderBeginTag(HtmlTextWriterTag.Td); // Td açlış takısı
output.AddAttribute(HtmlTextWriterAttribute.Href, items.Rows[i]["link"].ToString()); // href isimli attribute sonraki satırda açılacak olan A takısına ilave edilecek. Değeri ise link alanının içeriği olacak
output.RenderBeginTag(HtmlTextWriterTag.A);// A takısı açılıyor
output.Write(items.Rows[i]["title"].ToString()); // A takısı içine title alanının değeri yazılıyor
output.RenderEndTag(); // A takısı kapatılıyor
output.RenderEndTag(); // Td takısı kapatılıyor
output.RenderEndTag(); // Tr takısı kapatılıyor
}
output.RenderEndTag();// Table' ın bitiş takısı </table>
}
catch
{
output.Write("Adres çözümlenemedi");
}
#endregion
}
}</pre>
<p>Render metodunda, daha önceden Web sunucu kontrolü yazmak ile ilişkili makalelerimizde kullandığımızdan biraz daha farklı bir yol tercih ettik. Html içeriğini oluştururken, .Net içerisinde yer alan kuvvetli tiplerden faydalanırsak hatalı Html takısı yazma olasılığımız daha da azalacak ve özellikle tarayıcı farklılıklarını bertaraf edeceğizdir. Bu nedenle Render metodu içerisinde, <strong>HtmlTextWriter</strong> sınıfına ait <strong>RenderBeginTag</strong>, <strong> RenderEndTag</strong>, <strong>AddAttribute</strong>, <strong>AddStyleAttribute</strong> gibi metodlardan yararlanılmıştır. Örneğimize göre içeriğimiz bir Table elementi içerisinde tek sütundan oluşan bir yapıda olacaktır. Bu amaçla bir table takısı açmak için</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">output.RenderBeginTag(HtmlTextWriterTag.Table);</pre>
<p>kod satırından faydalanılmıştır. Burada RenderBeginTag, bir takının oluşturulmasını sağlarken ne çeşit bir element olacağını parametre olarak gönderdiğimiz <strong>HtmlTextWriterTag</strong> sabiti belirtmektedir. Html içerisinde açılan her takının kapatılması gerektiğini biliyoruz. Bunu kod tarafında ifade ederkende yine HtmlTextWriter sınıfının <strong>RenderEndTag</strong> metodundan faydalanmaktayız. Dikkat edilmesi gereken noktalardan biriside, Html elementlerine niteliklerin nasıl eklendiğidir. Dikkat ederseniz, bu amaçla <strong>AddStyleAttribute</strong> ve <strong>AddAttribute</strong> metodlarından yararlanılmıştır. Örneğin tablomuza arka plan rengi ve genişlik vermek için</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">output.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "WhiteSmoke");
output.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%");</pre>
<p>kod satırlarından faydalanılmaktadır. Hangi niteliğin ekleneceğini belirlemek için ise, <strong>HtmlTextWriterStyle</strong> sabitinden yararlanılır. AddAttribute metoduda benzer işlevselliğe sahip olmakla birlikte desteklediği nitelik tipleri daha farklıdır.</p>
<p>Gelelim metodun temel olarak yaptığı işe. İlk olarak kişiselleştirilebilen Url özelliğinden değer alınmakta ve <strong>XmlTextReader</strong> nesnesi elde edilmektedir. Hataların önüne geçmek amacıyla ilk olarak Url özelliğinin değerinin boş olup olmadığına bakılır. Bunun haricinde internet bağlantısının kesik olması gibi hallerde, istenen Url' den RSS bilgileri çekilemeyeceği için bir çalışma zamanı istisnası(runtime exception) alınacaktır. Bununda önüne geçmek için genel bir try-catch bloğu kullanılmıştır. Bu nedenle yükleme ve Render işlemlerini try bloğu içerisinde gerçekleştirmekteyiz. Burada Url adresinden elde edilen Xml içeriğini okumak için farklı yollarda tercih edilebilir.</p>
<p>Örneğin doğrudan XmlReader üzerinden hareket edilebilir veya <strong>XPathNavigotor</strong> tipinden faydalanılabilir ki bunlar performans açısından daha etkili yollardır. Biz kodu çok fazla karmaşıklaştırmamak adına doğrudan <strong>DataSet</strong> kontrolüne alıyor ve pahalı bir maliyetinde altına giriyoruz :) DataSet içerisinde yer alan channel ve item tablolarından faydalanarak, RSS içeriğindeki bilgilere erişebiliriz. Örneğin RSS sahibinin belirttiği başlığı (title), Html tablomuzun ilk satırındaki ilk hücreye alıyoruz. Her bir item elementinin belirttiği, başlık ve adres bilgilerini ise sırasıyla title ve link elementlerinden alıp bir <strong>a href </strong>elementi içerisinde gösteriyoruz. Bildiğiniz gibi a href elementi bir link oluşturulmasını sağlamaktadır.</p>
<p>Web Part kontrolümüzün, <strong>WebPart</strong> sınıfından gelen pek çok özelliğinide istersek ezebiliriz (override). Örneğin, Web Part bileşenimizin başlık bilgisi(title), imge bilgisi (title icon image) gibi değerlerini değiştirmek isteyebiliriz. Lakin bu tip üyelerin değerlerinin kontrolün Html çıktısının üretilmesinden önce atanması gerekir. Bu amaçla yapıcı metoddan, özelliğin set bloğundan yada kontrole ait PreInit olay metodundan faydalanabiliriz. Geliştirdiğimiz örnekte biz yapıcı metod ve özellik(property) kullanmayı tercih edeceğiz. Bu amaçla sınıfımıza aşağıdaki üyeleri eklememiz yeterli olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public override string Title
{
get
{
return _RssOwner;
}
set
{
_RssOwner= value;
}
}
public RssPart()
{
TitleIconImageUrl = "Bilgi.gif";
}</pre>
<p>Yukarıdaki kod parçasında dikkat ederseniz, WebPart sınıfında yer alan Title özelliği ezilmektedir ve kullanıcı tarafından kişiselleştirilebilen RssOwner özelliğinin kullandığı _RssOwner alanının değerini işaret etmektedir. Diğer taraftan, Web Part bileşenimizde hemen başlık kısmının yanında sembolik bir imge göstermek amacıyla yapıcı metod(constructor) içerisinde <strong> TitleIconImageUrl</strong> özelliğine bir değer ataması yapılmıştır. Örnek bir Asp.Net sayfasında Web Part bileşenimizi kullandığımızda tasarım zamanında iken, aşağıdakine benzer bir görüntü ile karşılaşırız.</p>
<p><img src="/makale/images/mk197_5.gif" alt="" width="164" height="128" border="0" /></p>
<p>Burada yer alan Bilgi.gif isimli resim, Web Control Library içerisinde yer almaktadır ve herhangibir Asp.Net projesinde ilgili Web Part bileşeni kullandığında otomatik olarak root klasör içerisine taşınacaktır. Bir başka deyişle eklenen ilk kontrol ile birlikte gelen web kontrol kütüphanesi referansı, beraberinde kaynak olarak bu resim dosyasınıda hedef web uygulaması içerisine taşıyacaktır. Yapılan bu son ekelemeler ile birlikte Web Part bileşenimizin yeni hali aşağıdaki sınıf diagramında gösterildiği gibi olacaktır.</p>
<p><img src="/makale/images/mk197_4.gif" alt="" width="276" height="346" border="0" /></p>
<p>Artık Web Part bileşenimizi bu haliyle test edebiliriz. Kişiselleştirmenin tam olarak etkilerini görebilmek için, bileşenimizi <strong>Membership</strong> ayarları yapılmış örnek bir Asp.Net Web uygulaması üzerinden test etmekte fayda olacaktır. Testleri gerçekleştirmek için ekran görüntüsü aşağıdaki gibi olan bir web sayfasından yararlanabiliriz.</p>
<p><img src="/makale/images/mk197_6.gif" alt="" width="261" height="495" border="0" /></p>
<p>Unutulmamalıdır ki, geliştirdiğimiz Web Part bileşeninin, Web Part Framework özelliklerini etkin bir şekilde kullanabilmesi için bir <strong>Web Part Zone</strong> içerisinde yer alması gerekir. Bu nedenle, RssPart isimli Web Part bileşenimizi zoneRss isimli bir WebPartZone kontrolü içerisinde ele alıyoruz. Diğer taraftan, Web Part kontrolümüz içerisinde yer alan Url ve RssOwner gibi çalışma zamanında kişiselleştirilebilir özelliklerini ele alabilmek için bir<strong> Editor Zone</strong> kontrolümüzde yer almaktadır. Örneğimizde sadece editör kısmını göz önüne alacağımızdan, sayfanın Page_Load olay metodu içerisinde <strong>WebPartManager</strong> bileşenimizin <strong>DisplayMode</strong> özelliği aşağıdaki gibi <strong>EditDisplayMode</strong> olarak ayarlanmıştır. Gerçek bir projede elbetteki kullanıcının diğer modlarıda seçebileceği şekilde kod yazmak gerekir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">wpmYonetici.DisplayMode = WebPartManager.EditDisplayMode;</pre>
<p>Sonuç olarak uygulamamızı çalıştırdığımızda, aşağıdaki Flash animasyonunda yer alana benzer bir sonuç ile karşılaşırız.<em>(Flash dosyasının boyutu 380 Kb tır. Bu nedenle yüklenmesi zaman alabilir.)</em></p>
<p><object id="obj1" width="601" height="581" classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" border="0">
<param name="movie" value="http://www.buraksenyurt.com/makale/images/mk197_Video.swf" />
<param name="quality" value="High" /> <embed type="application/x-shockwave-flash" width="601" height="581" src="http://www.buraksenyurt.com/makale/images/mk197_Video.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" name="obj1" /></object></p>
<p>Dikkat ederseniz, iki ayrı kullanıcı için iki ayrı RSS bilgisi ele alınabilmektedir. Burak isimli kullanıcı kendisi için Msdn' e ait RSS içeriğini tutarken, Melike isimli kullanıcıda C#Nedir? sitesine ait RSS bilgisini ele alabilmektedir. Bu bilgilerin arka tarafta nereye yazıldığını kontrol etmek istersek, <strong> Membership</strong> bilgilerinin tutulduğu veritabanında <em>(ki örneğimizde local AspNetDb.mdf dosyası kullanılmaktadır. Bir başka deyişle üyelik bilgileri web uygulamasın ait App_Data klasöründe yer alan AspNetDb.mdf veritabanı içerisinde saklanmaktadır.)</em> yer alan <strong> AspNet_PersonalizationPerUser</strong> tablosuna bakmamız yeterli olacaktır.</p>
<p><img src="/makale/images/mk197_7.gif" alt="" width="564" height="103" border="0" /></p>
<p>Kendi Web Part bileşenlerimizi geliştirirken başka üst sınıf özelliklerini ezebilir ve istediğimiz şekilde çalışmalarını sağlayabiliriz. Aynı zamanda, bir Web Part bileşenine çalışma zamanında ele alacağı yeni fiili aksiyonlar <strong>(verb)</strong> ekleyebiliriz. Bu ve benzeri diğer konuları ilerleyen makalelerimizde ele almaya çalışacacağız. Böylece geldik bir makalemizin daha sonuna. Bu makalemizde basit olarak kendi Web Part bileşenlerimizi nasıl yazabileceğimizi incelemeye çalıştık. Bunun için,</p>
<ul>
<li>kontrolümüzü tanımlayacak olan sınıfı WebPart isimli abstract sınıftan türetmemiz gerektiğini,</li>
<li>içeride kullanıcı bazında kişiselleştirme yapacağımız özellikler için Personalizable niteliğini (attribute) kullanmamız gerektiğini,</li>
<li>kontrolün kişiselleştirilebilir özelliklerinin çalışma zamanında ele alınması için PropertyGridEditorPart kullanmamız gerektiğini,</li>
<li>özelliklerin PropertyGridEditorPart içerisinde gözükmesi için WebBrowsable niteliğinin kullanılması gerektiğini,</li>
<li>istersek var olan Web Part özelliklerini ezerek değiştirebileceğimizi,</li>
<li>her zaman olduğu gibi kontrolün Html çıktısını manuel olarak düşünememiz ve yazmamız gerektiğini,</li>
</ul>
<p>öğrendik. Bir sonraki makalemizde görüşmek dileğiyle hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/makale/images/CustomWebParts.rar">Örnek Uygulama İçin Tıklayınız</a></p>2007-03-28T12:00:00+00:00asp.net 2.0web partscustom web partsbsenyurtWeb uygulamalarında var olan bileşenlerin yetersiz kaldığı durumlarda kendi kontrollerimizi geliştirme yoluna gidebiliyoruz. Kendi kontrollerimizi geliştirirken seçebileceğimiz yollar bellidir. Var olan bir web bileşeninden türetme yolunu seçebiliriz (Inherited Controls).https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=be5257e7-8d7d-4edf-b6f7-03749ceb1af60https://buraksenyurt.com/trackback.axd?id=be5257e7-8d7d-4edf-b6f7-03749ceb1af6https://buraksenyurt.com/post/Kendi-Web-Part-Bilesenlerimizi-Gelistirmek-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=be5257e7-8d7d-4edf-b6f7-03749ceb1af6