Bir ASP.NET Sayfasının HTML Çıktısını Değiştirmek

Bir web sayfasının HTML çıktısını bazı özel durumlarda programatik olarak elde etmek veya değiştirmek isteyebiliriz. ASP.NET'in sayfa yapısı ve HTML oluşturma şekili ilk bakışta biraz karmaşık görünse de, gelişmiş özellikleri sayesinde bir web sayfasına ait HTML çıktıyı programatik olarak ele almamıza ve değiştirmemize olanak sağlamaktadır. Peki neden HTML çıktıyı değiştirmek isteyelim? Birkaç mantıklı nedeni şöyle sıralayabilirim:

- ASP.NET'in oluşturduğu bazı HTML kodlarına tasarım aşamasında veya sunucu taraflı kodlama ile fazla müdahale edemeyiz. Ancak bazı durumlarda belirli HTML elementlerinin veya bir sunucu kontrolünün oluşturduğu HTML kodunu elde etmek, değiştirmek isteyebiliriz. Örneğin ViewState nesnesinin sayfadaki yerini değiştirmek bu durum için güzel bir örnektir.
- Bir uygulamada fazla sayıda sayfa varsa, sayfaların tamamında belirli değişiklikler yapmak isteyebiliriz. Örneğin HTML standartlarına uymayan bir yazım şeklini farkedip, sonradan tüm sayfalardaki bu hatalı yazımı kolayca düzeltmek...
- Belirli ifadelerin geçtiği sayfalarda değişiklik yapmak istenebilir. Örneğin Ana Sayfa kelimelerinin geçtiği sayfalarda bu ifadeyi Anasayfa olarak değiştirmek...

Bu tip durumlarda tek tek dosyaları güncellemek yerine daha hızlı ve dinamik bir çözüm yolu aranabilir. HTML çıktıyı değiştirmenin yanında, çıktıyı belirli bir kaynağa(fiziksel olarak bir konuma veya veritabanına) kaydetmek de istenilebilir. Özellikle raporlama amaçlı oluşturulan sayfalarda, belirli zaman aralıklarıyla HTML çıktılarını kaydetmek ve ilerleyen tarihlerde incelemek gibi taleplerle karşılaşılabilir. Aslında bu tip ihtiyaçlar duruma göre, geliştirilen projeye göre çok farklı şekillerde günlük hayatımızda karşımıza çıkabiliyor.

ASP.NET uygulamalarında, bir sayfanın HTML çıktısına Response.Filter özelliği ile erişebiliriz. Bu özellik System.IO.Stream tipinden bir değer döndürmektedir. Her ne kadar Response.Filter gibi yazımı çok kolay olan bir yolla HTML çıktıya erişiyor gibi görünse de, bu çıktıya erişmek, hatta çıktıyı değiştirmek için biraz daha zahmetli bir yol izlememiz gerekecektir. Stream nesnesi ile dosya okuma tecrübesi olanlar ne demek istediğimi daha iyi anlayacaktır. Örneğimize başlamadan önce basit bir sayfa tasarlayıp oluşturacağı HTML çıktıyı inceleyelim. Aşağıda örnek sayfamızın kodları yer almaktadır.

Default.aspx
<%@ 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></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        Mail adresim:<br>
        testmaili@nedirtv.com
    </div>
    </form>
</body>
</html>

Sayfa üzerinde dinamik olarak bir işlem yapmadığımız için <div> elementinin iç kısmı oluşacak HTML çıktıda aynen yer alacaktır. Sayfadaki amacımız <br> şeklinde yazılmış elementleri <br /> şekline getirmek ve içerisinde testmaili@nedirtv.com gibi @ işareti ile yazılmış mail adreslerini sayfamızı indeksleyen örümcek yazılımlardan koruma için testmaili (at) nedirtv.com şekline getirmek olacak.

Çıktıyı değiştirmeden önce çıktıyı nasıl elde edip, string bir değişkene atabileceğimize bakalım. Response.Filter özelliği Stream tipinden bir nesne taşımaktadır. Bu nesnenin içeriğini elde edebilmek için özel bir Stream nesnesi yazmak ve Response.Filter'ın içeriğini bu nesne üzerinden istemciye gitmesini sağlamamız gerekir. Dolayısıyla ilk olarak Filter özelliğine atayabileceğimiz tipten, yani Stream sınıfından kalıtılmış bir sınıf yazacağız. Aşağıda HtmlFilterStream adındaki özel sınıfımıza ait kodlar yer almaktadır. Kodlar kalabalık görünse de, makalenin ilerleyen kısımlarında sadece Flush ve Write metotlarının içeriklerinde değişiklikler yapacağız.

App_Code/HtmlFilterStream.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
public class HtmlFilterStream : Stream
{
    Stream _baseStream;
    long _position;
    string _html = "";
    public HtmlFilterStream(Stream stream)
    {
        _baseStream = stream;
    }
    public override bool CanRead { get { return true; } }
    public override bool CanSeek { get { return true; } }
    public override bool CanWrite { get { return true; } } 
    public override long Length { get { return 0; } }
    public override long Position
    {
        get { return _position; }
        set { _position = value; }
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        _baseStream.Write(buffer, 0, buffer.Length);
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        return _baseStream.Read(buffer, offset, count);
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        return _baseStream.Seek(offset, origin);
    }
    public override void SetLength(long value)
    {
        _baseStream.SetLength(value);
    }
    public override void Flush()
    {
        _baseStream.Flush();
    }
}

Sınıfımızı Stream sınıfından kalıttığımız için Flush, Length, Read, Write gibi abstract üyeleri ezmemiz(override) gerekiyor. Üyelerin içlerini yukarıdaki kodlarda görüldüğü şekilde dolduruyoruz. Response.Filter özelliğinden alacağımız nesnenin kendine ait üyeleri çağırabilmek içinse _baseStream adından bir field tanımlıyor ve sınıfın yapıcı metodunda(constructor) bu nesneyi parametre olarak alıyoruz. Bir de _html adında bir field tanımlamamız var, bu field az sonra okuma işlemlerinde kullanmamız için gerekli olacak.

Gelelim HTML çıktıyı yakalama işlemine. İlk olarak sadece çıktıyı okumaya çalışacağız. ASP.NET sayfalarının çıktıları oluşurken sayfa parçalar halinde render edilmekte ve Write metodu çalışma zamanı içerisinde birden fazla defa tetiklenebilmektedir. Her tetiklenmede HTML çıktının belirli bir parçası elde edildiği için bu metotta HTML kodlarının parçalarını birleştirmemiz gerekecek. Flush metodu ise açılan stream'e ait bilgilerin bellekten kaldırılmasından hemen önce tetikleneceği için bu metotta elde edilen HTML kodlarını tamamına erişebileceğiz. Write ve Flush metotlarında yapacağımız değişiklikler aşağıda görülmektedir.

public class HtmlFilterStream:Stream
{
    ...
    public override void Write(byte[] buffer, int offset, int count)
    {
        _html += Encoding.Default.GetString(buffer, offset, count);
        _baseStream.Write(buffer, 0, buffer.Length);
    }
    ... 
    public override void Flush()
    {
        string output = _html;
        _baseStream.Flush();
    }
}

Write metodunda üretilen HTML çıktıları birleştirdik ve son olarak Flush metodunda tüm çıktıyı okuduk. Eğer çıktıyı belirli bir kaynağa kaydetmek istersek Flush metodu içerisinde _html değişkeninin içeriğini kullanabiliriz. Çıktıyı yakalamak için gerekli kodları hazırladık, ancak halâ ASP.NET sayfamızın HTML çıktısını bu nesne üzerinden stream edilmesini sağlamış değiliz. Bu işlemi gerçekleştirmek için de Default.aspx dosyasının Page_Load metoduna aşağıdaki satırı eklememiz yeterli.

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Filter = new HtmlFilterStream(Response.Filter);
    }
}

Response.Filter'a ait nesne referansının yeni bir Stream nesnesi(HtmlFilterStream) olacağını belirledik. Hatırlayacağınız gibi kendi yazdığımız HtmlFilterStream nesnesini yapıcı metodunda  parametre olarak bir Stream nesnesi istiyorduk. new HtmlFilterStream(Response.Filter) ifadesiyle sayfanın HTML çıktısına ait ana stream'i yeni üretilen nesneye göndermiş olduk.

HtmlFilterStream.cs dosyasında Flush metodunun içerisindeki bir satıra breakpoint ekleyip sayfayı çalıştıracak olursak _html değişkeninde üretilen sayfanın HTML kodlarını görebiliriz.


Resim: Debug modda _html değişkeni içerisindeki HTML kodları

Write metodu HTML çıktının stream'e aktarıldığı yerdir. Dolayısıyla HTML çıktıyı değiştirmek için Write metodunda değişiklikler yapmamız gerekiyor. HTML çıktının Response'a yazıldığı kısım ise şu anki Write metodunun en alt satırında yer alan _baseStream.Write metodu aracılığıyla yapılmaktadır. Bu metodun aldığı buffer parametresi çıktıyı içerisinde saklayan byte dizisidir. O halde çıktıyı değiştirmek için öncelikle _html değişkeninde değişiklikler yapmak, sonra da bu değişiklikleri buffer dizisine aktarmak gerekecek. Aşağıda Write metodunun yeni hali görülmektedir.

...
using System.Text;
using System.Text.RegularExpressions;
public class HtmlFilterStream:Stream
{
    ...
    public override void Write(byte[] buffer, int offset, int count)
    {
        _html += Encoding.Default.GetString(buffer, offset, count);
        // <br> şeklinde yazılmış elementleri <br/> biçimine dönüştürüyoruz
        _html = _html.Replace("<br>""<br/>");
        
        // E-posta adreslerini değiştirmek için gerekli metin formatını belirliyoruz
        string emailPattern = @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
        Regex objRegex = new Regex(emailPattern);
        MatchCollection objCol = objRegex.Matches(_html); // Formata uyan metinleri buluyoruz
        foreach (Match item in objCol)
        {
            //İfade içerisindeki @ karakterlerini (at) haline dönüştürüyoruz
            string newValue = item.Value.Replace("@"" (at) ");
            _html = _html.Replace(item.Value, newValue);
        }
        buffer = Encoding.Default.GetBytes(_html); // Güncel _html içeriğini byte dizisine çeviriyoruz
        _baseStream.Write(buffer, 0, buffer.Length); // Stream'i yeni içeriğiyle yazdırıyoruz
    }
}

Hatırlanacağı gibi dosyadaki amacımız <br> elementlerini <br/> şekline dönüştürmek ve e-posta adreslerinin içerisindeki @ karakterini (at) şeklinde yazdırmaktı. Bu doğrultuda iki değişiklik için gerekli düzenlemeleri yaptık. Son olarak Encoding.Default.GetBytes metodunu kulllanarak _html string değişkenindeki bilgileri buffer dizisine aktardık ve çıktıyı stream'e bastık. Sayfamızı çalıştırdığımızda değişikliklerin yapıldığını görebileceğiz.


Resim: Sağ kısımda HTML çıktının değiştiği görülmektedir

Yaptığımız HTML çıktıyı değiştirme işlemi sadece Default.aspx sayfası ile ilgiliydi. Default.aspx sayfasının Page_Load metodunda Response.Filter özelliğini yeni bir stream nesnesi üzerinden ele aldığımız için sadece bu sayfa değişiklikten etkilenecektir. Bu değişikliğin uygulamadaki tüm sayfalar için yapılmasını istiyorsak özel bir HttpModule nesnesi yazmamız veya Global.asax dosyası içerisinden ilgili Application olay metodunda(BeginRequest veya PostReleaseRequestState gibi) gerekli gerekli düzenlemeleri yapmamız gerekiyor. Aşağıda Global.asax dosyasına ekleyeceğimiz kodlar yer almaktadır.

<%@ Application Language="C#" %>
<script runat="server">
    ...
    void Application_PostReleaseRequestState(object sender, EventArgs e)
    {
        if(Response.ContentType == "text/html")
            Response.Filter = new HtmlFilterStream(Response.Filter);
    }
</script>

Bu güncellemeyi yaptıktan sonra projemize farklı .aspx dosyaları ekleyip HTML çıktının düzenlendiğini gözlemleyebiliriz. İlgili olay metodunda uzantıya yönelik değil, çıktının tipine göre(text/html kontrolü) işlem yapıldığı için .ashx gibi dosyalarında çıktıları ele alınacaktır.

Not: Yazıyı hazırlarken http://www.4guysfromrolla.com/articles/120308-1.aspx adresindeki bilgilerden faydalandım.

26 Temmuz 2010 Pazartesi 17:07

Yorum - RSSYorumlar (0)

Etiket: ,
Kategori: ASP.NET

facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Microsoft Ajax Library - Script Loader

Microsoft Ajax Library ile istemci tarafında bir işlem yapıldığında, bu işlemle ilgili script dosyalarının istemciye yüklenmesini sağlayan Script Loader adını verdiğimiz bir yapımız var. Önceki yazılarımda da bahsettiğim gibi Microsoft Ajax Library içerisinde 200'e yakın script dosyası yer almakta. Bu dosyaların bazıları çekirdek(core) işlemleri içeren ve çoğu AJAX uygulamasında kullanılan dosyalar iken, bazıları ise sadece özel durumlarda, özel nesneler için kullanılan dosyalar. Dolayısıyla böyle geniş bir dosya arşivi üzerinde uygulama geliştirirken hangi dosyayı kullanmak zorunlu, şu durumda hangi dosyayı da eklemek gerekir gibi sorularla vakit kaybetmek, eksik dosyalar yüzünden script hataları alarak dakikalarca uğraşmak can sıkıcı olabiliyor.

Script Loader ise getirdiği kolaylık sayesinde bizi dosya bazında düşünmek yerine, yapılacak işlem bazında hareket etmemizi sağlıyor. Şöyle ki, siz Script Loader'a o sayfada yapacağınız işlemleri, kullanacağınız nesneleri belirtiyorsunuz, o arka planda gerekli olan bütün dosyaları yüklüyor. Yükleme işlemini yaparken bir dosyayı sadece ihtiyaç duyulduğunda indirdiği gibi(lazy loading), aynı dosyaya birden fazla talep atılmasını da engelliyor. Ajax kütüphanesi içerisinde Sys.require fonksiyonunu kullaranak Script Loader mekanizmasını harekete geçirebiliyoruz. Örneğin aşağıdaki kod parçasında sayfa üzerinde bir TextBox’a ColorPicker kontrolü bağlamak için gerekli olan kodlar bulunmakta.

<link href="Scripts/Extended/ColorPicker/ColorPicker.css" rel="stylesheet" type="text/css" />

<script src="Scripts/Start.js" type="text/javascript"></script>

<script src="Scripts/Extended/ExtendedControls.js" type="text/javascript"></script>

 

<script type="text/javascript">

    Sys.require(Sys.components.colorPicker, function () {

        Sys.create.colorPicker("#color1", null);

    });

</script>


Kodlarda Sys.require fonksiyonu ile colorPicker bileşeni yüklenmiş ve yükleme işlemi esnasında da bir alt satırdaki ifade ile color1 adındaki text input kontrolü ColorPicker kontrolüne bağlanmış durumdadır. Belirtmek istediğim bir diğer nokta da her AJAX sayfasına eklenmesi zorunlu olan dosya/dosyalar bulunmaktadır. Kod parçasının 2. ve 3. satırına bakacak olursanız Start.js ve ExtendedControls.js adındaki dosyalarında eklendiğini göreceksiniz. Start.js her AJAX sayfasına eklenmesi gereken script dosyasıdır, sayfa içerisinde kullanacağınız temel JavaScript nesneleri ve fonksiyonları bu dosyada yer almaktadır. Ayrıca sayfada AJAX Control Toolkit’ten tanıdığımız kontrolleri(Calendar, ColorPicker, AutoComplete… gibi) kullanmak istiyorsanız bunun için de ExtendedControls.js dosyasını eklemeniz gerekmektedir. Bunların dışında kalan dosyalar ise Script Loader tarafından ihtiyaç duyulduğunda istemciye gönderilecektir.

Yukarıdaki kod parçasında Script Loader’a sadece colorPicker bileşenini yüklemesi gerektiğini söyledik. Birden fazla işlem için farklı Sys.require çağrımları yapabileceğiniz gibi, gerekli durumlarda bir çağrı üzerinden birden fazla bileşenin de yüklenmesini sağlayabilirsiniz. Örneğin aşağıdaki kod parçasında aynı çağırım içerisinde dataView bileşeni ve WebService scriptleri yüklenmekte. Görüleceği üzere köşeli parantez arasına yazılan birden fazla ifade ile bu tip çağrımlar yapılabiliyor.

Sys.require([Sys.components.dataView, Sys.scripts.WebServices], function () { ...

 

26 Mart 2010 Cuma 10:40

Yorum - RSSYorumlar (0)

Kategori: ASP.NET

facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Microsoft Ajax CDN

Microsoft geçtiğimiz aylarda web uygulama geliştiriciler için kendi CDN(Content Delivery Network) hizmetini duyurmuştu. Bu hizmet sayesinde artık uygulamalarımızda kullanmak istediğimiz Microsoft Ajax Library ve jQuery script dosyalarını doğrudan proje içerisinde saklamaya ihtiyaç duymuyoruz. Şöyle ki, bu hizmet zaten bahsi geçen dosyaların Microsoft sunucuları üzerinde saklanmasını ve uygulamaların bu dosyaları referans olarak alması durumunda ilgili kütüphanelerin kullanabilmesini sağlıyor. Bu çok mu gerekli diye sorabilirsiniz, cevabı tamamen sizin ihtiyacınıza bağlı. Sonuçta aynı dosyaları kendi projeniz içerisinde barındırdığınızda, istediğiniz zaman istediğiniz kısımlarını değiştirme, güncelleme şansına sahipsiniz. Ancak diğer tarafta bahsettiğimiz kütüphanelerde zaten çok ciddi ekipler tarafından geliştirilen ve üzerinde çok da fazla değişiklik yapılmaya ihtiyaç duyulmayan kütüphaneler. Dolayısıyla bu kütüphanelerin güvenilir bir CDN hizmeti üzerinden kullanımı performans ve uygulamadaki dosya karmaşasını azaltmak adına doğru hareketler olacaktır. CDN hizmetini sunan kurum dünyanın farklı konumlarında bu dosyaları barındırması için belki de binlerce sunucu kullandığı için sizin uygulamanıza gelen talepler load balancer'lar aracılığıyla en hızlı hizmet sunacak konumdaki sunucuya yönlendiriliyor.

Microsoft Ajax Library olsun jQuery olsun eklenti dosyaları ile birlikte ciddi büyüklüğe sahip(Microsoft jQuery’nin core dosyaları dışındaki eklenti dosyaları için CDN hizmeti sunar mı, şimdilik bilinmiyor) Dolayısıyla bu kadar dosyayı sunucuda barındırmak özellikle JavaScript dosyaları için hiyerarşi karmaşasına yol açabilir. Böyle bir karmaşa içerisinde çalışmak istemeyenler için şiddetle Microsoft, Yahoo, Google, Amazon gibi sağlayıcıların CDN hizmetlerini kullanmalarını tavsiye ederim. Microsoft AJAX CDN için bu sayfadan gerekli bilgileri alabilirsiniz.

Gelelim basit bir örnek ile Microsoft CDN'i test etmeye. Aşağıdaki kod parçalarında bir text input kontrolüne Microsoft Ajax Library'de yer alan Calendar bileşenini bağlıyorum. Dikkat edeceğiniz gibi CSS ve JavaScript dosyalarının konumları ajax.microsoft.com adresi üzerinden verilmiş.

<link href="http://ajax.microsoft.com/ajax/beta/0911/extended/Calendar/Calendar.css" rel="stylesheet" type="text/css" />

 

<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/Start.js"></script>

<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/extended/ExtendedControls.js"></script>

 

<script type="text/javascript">

    Sys.require(Sys.components.calendar);

 

    Sys.onReady(function () {

        Sys.create.calendar("#txt1");

    });

</script>

Visual Studio'da JavaScript kodlarını yazarken dahi intellisense özelliğinin tıkır tıkır çalıştığını, Microsoft Ajax Library'de yer alan nesne ve fonksiyonlara kolayca erişebildiğinizi göreceksiniz. Sayfayı çalıştırırken Fiddler gibi bir web debugger uygulaması kullanarak sayfayı izlerseniz olayın detaylarını da güzel bir şekilde görebilirsiniz.

Göreceğiniz gibi kırmızı kutu içerisindeki dosyalar sadece bizim kod içerisinde referansını verdiğimiz dosyalar. Önceki yazımın Script Loader kısmında belirttiğim gibi Microsoft Ajax Library çalışma anında ihtiyacı duyduğu dosyaları da istemciye yüklemektedir. İşte resimde mavi kutu içerisinde gördüğünüz dosyalar da diğer script dosyalarıdır. Tüm dosyaların Host kolonundaki adres bilgilerine bakacak olursanız dosyaların ajax.microsoft.com adresinden çekildiğini görebilirsiniz. Sonuç olarak sorunsuz ve daha hızlı çalışan bir AJAX uygulaması elde etmiş olduk.

17 Ocak 2010 Pazar 14:54

Yorum - RSSYorumlar (1)


facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Microsoft Ajax Library (Yoksa ASP.NET AJAX 4.0 mı Desek?)

Geçtiğimiz ay içerisinde gerçekleşen PDC’09 konferansında Microsoft, ASP.NET AJAX adındaki AJAX kütüphanesini AJAX Control Toolkit ile birleştirerek artık Microsoft AJAX Library adında bir ürün ile yola devam edeceğini açıkladı. Microsoft AJAX Library! Kulağa hoş geliyor. Şu an Beta sürümü Codeplex üzerinden indirilip kullanılabilen bu kütüphane sadece ASP.NET WebForm uygulamalarında değil, ASP.NET MVC, PHP, Ruby on Rail gibi tüm web uygulamalarında kullanılabilir hale getiriliyor. Aslında ASP.NET AJAX jQuery’leşiyor diyebiliriz bu değişikliğe. Her ne kadar .NET Framework’den ayrı olarak geliştirilse de, ASP.NET 4.0 ile birlikte bu ürünün resmen duyurulması bekleniyor. Ben de geçtiğimiz haftalarda gerçekleştirdiğimiz INETA NEXT etkinlikleri vesilesiyle Microsoft Ajax Library’i inceleme şansına sahip oldum.

Microsoft Ajax Library ile neler değişiyor, ne gibi yenilikler geliyor. Şöyle kısaca özetlemeye çalışayım. İlerleyen zamanlarda fırsat bulursam belki detaylı yazılara da yer verebilirim.

Büyük ve düzenli bir JavaScript kütüphanesi: Temel Ajax kütüphanesi dışında Control Toolkit kontrollerinin istemci tarafında kullanılmasına olanak sağlaması nedeniyle JavaScript arşivi oldukça genişlemiş durumda. Debug dosyaları ile birlikte yaklaşık 200 .js dosyasına sahip bir kütüphanemiz var artık. Tabi ki akla ilk gelen bu dosyaların tamamını veya büyük kısmını uygulamamızda saklamak durumunda mıyız? İsterseniz saklayabilirsiniz, istemezseniz alttaki maddeyi okumanızı tavsiye ederim :)

Microsoft Ajax CDN: Microsoft kendi Ajax kütüphanesini ve jQuery kütüphanesini yine geçtiğimiz aylarda açtığı CDN(Content Delivery Network) hizmeti üzerinden ücretsiz olarak sunmakta. Yani yukarıda bahsettiğimiz JavaScript dosyalarının hiçbirini uygulamanızda barındırmadan kullanabilme şansına sahipsiniz. Bu hizmeti kullanarak uygulamanızın performansını daha iyileştirebileceğiniz gibi, sitenizin gereksiz bandwith tüketimini azaltmış olabiliyorsunuz. Yapmanız gereken tek şey .js dosyalarının referansını Microsoft’un sunucuları üzerinden vermek. Üstelik Visual Studio üzerinde çalışırken internete bağlı iseniz JavaScript intellisense özelliğini aynen kullanabiliyorsunuz.

Script Loader: Ajax Library’de yer alan script loader fonksiyonu(Sys.create) ile ihtiyaç duyduğunuz script dosyalarının sayfanın yüklenmesi esnasında istemciye gönderilmesi sağlanabiliyor. Nasıl yani, anlamadım demiş olabilirsiniz. Şöyle ki, malum artık kalabalık bir istemci kütüphanemiz var. Yapılacak bir işlem için veya oluşturulacak bir nesne için hangi JavaScript dosyaları gerekli sorusunu Script Loader sayesinde düşünmeye pek gerek kalmıyor. Sayfaya sadece temel script dosyalarını ekledikten sonra gerekli işlem veya kontrol ile ilgili çağırımları yaptığınızda arka planda gerekli tüm script dosyaları istemciye gönderilmekte. Örneğin Sys.require([Sys.components.dataView, Sys.scripts.WebServices]); ifadesi ile hem dataView nesnesini kullanacağınızı, hem de bir web servisine bağlanma işlemi yapacağınızı söylüyorsunuz, script loader da arka planda gerekli olan tüm script dosyalarını otomatik olarak istemciye yüklüyor.

İstemci Tarafında Data Yükleme(Client-side Data Binding): dataView adındaki nesne sayesinde artık istemci tarafında data binding işlemi yapılabiliyor. Microsoft Ajax Library’deki en göze batan yeniliklerden olan dataView nesnesi, arka planda bir AJAX talebi ile getirilen verilerin doğrudan istemci tarafında HTML koduna render edilmesini sağlıyor. Özellikle yoğun veri transferi yapılan iş uygulamalarında network trafiğini azaltmak ve hız adına müthiş bir yapı sunan bu özellik ile AJAX işlemleri daha performanslı hale geliyor. Diğer yandan dataView nesnesi sayfa ilk yüklendiğinde statik olarak HTML kodu içerisinde veri saklamayıp, {{ProductName}} şeklinde tanımlayıcıları bulundurduğu için, bu yapının arama motorları açısından bir dezavantaj olduğunu söyleyebiliriz. Tabi ki ilerleyen zamanlarda arama motorları bu tarz string veriler üzerinde özel indekslemeler yapacaktır diye temenni ediyoruz.

jQuery Entegrasyonu: Farklı JavaScript kütüphanelerine kolayca entegre edilebilen jQuery’nin Microsoft Ajax Library ile birlikte çalışması da sağlanmış durumda. Mesela jQuery seçicisi ile seçilen nesneye Ajax Library’de yer alan bir kontrolü kolayca bağlayabiliyorsunuz. Örneğin gerekli öntanımlamalar yapıldıktan sonra yazılan $("#txt1").calendar(); ifadesi jQuery ile seçilen txt1 elementine calendar nesnesi bağlamaktadır.

Client DataContext ve AdoNetDataContext: Açıkcası bu nesnelerle ilgili örnek yapma şansım olmadı, ama madem böyle bir yazı yazdık eksik madde kalmasın:) Burada belirtilen DataContext nesneleri ile bir web servisi veya Data Service üzerinden yapılan veri işlemleri iki yönlü olarak kolayca gerçekleştirilebilmekte. Somutlaştırmak gerekirse; web servisinden getirdiğiniz verileri istemciye yükledikten sonra, istemcinin bu veriler üzerinde yaptığı değişiklikleri ekstra efor sarfetmeden yine web servisi üzerinden sunucuya gönderebilmeniz mümkün. Tabi ki burada seçme ve güncelleme işlemini yapacak iki ayrı web metoduna ihtiyaç duyulacaktır.

Yine belirtilmekte fayda duyduğum önemli bir hususta Microsoft’un bu ürünün arkasında resmi olarak durması ve tam destek vereceğini belirtmesi. Bilindiği gibi bu zamana kadar Ajax Control Toolkit Microsoft tarafından resmi ürün olarak lanse edilmiyordu. Ajax Library içinde yer alan bu kütüphanede artık destek kapsamında. Görüldüğü gibi .NET Framework 4.0’ın çıkması ile sadece sunucu tarafında değil, istemci tarafında da çalışmamız gereken bir hayli ev ödevimiz var. İlerleyen zamanlarda bu konulara daha detaylı şekilde değinmek dileğiyle...

4 Ocak 2010 Pazartesi 00:20

Yorum - RSSYorumlar (3)


facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

ASP.NET’te jQuery ile AJAX İşlemleri

jQuery artık web uygulamalarında sıklıkla kullanılır hale geldi. Gerek sayfa tasarımına harika görseller ve efektler katması, gerekse AJAX taleplerini basit ve hızlı gerçekleştiriyor olması bu kütüphaneyi programcılar ve tasarımcılar için çok cazip hale getiriyor. Son zamanlarda jQuery ile bir web servisinin veya PageMethod'un nasıl çağrılabileceği şeklinde sık sık sorular duyunca ben de jQuery ile ASP.NET uygulamalarında farklı AJAX taleplerinin nasıl yapılabileceği ile ilgili ufak bir uygulama hazırladım. Bu doğrultuda bu yazıda da;

  • ASP.NET sayfasına
  • Parametre alan bir ASP.NET sayfasına
  • Web servisine
  • PageMethod'a

jQuery ile nasıl AJAX talepleri yöneltebileceğimizi inceleyeceğiz.

Öncelikli olarak jQuery kütüphanesinde AJAX taleplerini gerçekleştireceğimiz ajax isimli fonksiyonu tanıyalım. ajax fonksiyonu parametre olarak talep yapılacak url, varsa parametre verilerini, bu verileri iletme yöntemini(GET,POST gibi), talep sonucunda okunacak verinin tipi gibi bilgileri alır. Bunların dışında işlemin başarılı gerçekleşmesi durumunda sayfada güncelleme yapılmasını sağlayan veya hata oluşması durumunda gerekli işlemlerin yapılmasını sağlayan iki fonksiyon tanımlamasını da kendi içerisinde bulundurabilir. Burada saydığımız parametrik değerlerin tamamı zorunlu değildir, yapılan talepteki duruma göre sadece ihtiyaç duyulan parametreler kullanılabilir. Aşağıda AJAX talebi için kullanılabilecek basit bir metot çağırımı görülmektedir.

$.ajax({

    type: 'GET',

    url: 'Sayfa.aspx',

    success: function(result) {

        $('#sonuc').html(result);

    },

});

Parametrelerin ne anlama geldiğine bakalım:

type: Yapılacak talebin veri iletim yöntemi. GET, POST gibi.
url: Asenkron talebin yapılacağı adres.
success: Talebin başarılı bitmesi durumunda çalışacak fonksiyon. Burada talep sonucunda gelen veriler sayfaya işlenir.

Bunlar dışında kullanılabilecek birkaç parametre daha var, bu parametrelere ilerleyen kısımlarda değineceğiz. Buradaki success parametresinin değer olarak aldığı fonksiyon tanımlamasını C# dilindeki isimsiz metotlara(anonymous methods) benzetebiliriz. success metodundaki result değişkeni AJAX talebi sonucunda okunan veriyi taşımaktadır. Bu fonksiyon içerisindeki $('#sonuc') ifadesi de sayfamızdaki sayfa id'sini taşıyan HTML elementini işaretleyen bir jQuery seçici ifadesidir. Dönen AJAX cevabı sonuc isimli HTML elementinin içeriğine yazdırılacaktır. Artık bir örnek ile bu talepleri nasıl yönetebileceğimizi incelemeye başlayalım.

NOT: jQuery'de seçiciler, olaylar gibi temel kavramlar hakkında detaylı bilgi almak isterseniz size jQuery'nin resmi sitesini veya bu konuda zengin Türkçe içeriğe sahip olan www.eburhan.com adresini incelemenizi tavsiye ederim.

NOT: jQuery'de ajax() fonksiyonu dışında get() ve post() isimli fonksiyonlarla da AJAX talepleri yapılabilmektedir. Aynı işlemleri yapan fakat farklı kullanım şekilleri olan get ve post isimli jQuery fonksiyonları ile ilgili de araştırma yapabilirsiniz.

ASP.NET Sayfasına Talepte Bulunmak

jQuery fonksiyonunu oluşturmadan önce sayfamız içerisindeki HTML elementlerini hazırlayalım.

Default.aspx

<input type="text" id="text1" />

<input type="button" value="Seç" onclick="CallAspxPage();" /><br />

<span id="sonuc"></span>

Butona tıklandığında çağrılacak JavaScript fonksiyonu içerisinde jQuery ile yapılacak çağrının cevabı sonuc adındaki alana yazdırılacak. Geldik işin asıl önemli olan kısmına; jQuery ajax fonksiyonunun yazımına. Aşağıda basit olarak bir ASP.NET sayfasına yapılacak jQuery talebi görülmektedir.

Default.aspx

<script type="text/javascript">

    function CallAspxPage() {

        $.ajax({

            type: 'GET',

            url: 'Ajax1.aspx',

            success: function(result) {

                $('#sonuc').html(result);

            },

            error: function() {

                alert('Talep esnasında sorun oluştu. Yeniden deneyin');

            }

        });

    }

</script>

url kısmında yer alan Ajax1.aspx isimli dosyaya talepte bulunuyor ve dönen cevabı sonuc isimli span elementine yazdırıyoruz. Burada az önce gördüğümüz standart ajax fonksiyonu tanımlamasının dışında bir de error adında bir parametreyi görüyoruz. error parametresine bağlanan isimsiz metot ise talep esnasında bir hata ile karşılaşılırsa çalışacak. Sayfayı çalıştırıp butona tıklamamız durumunda sonuc isimli elementin içeriğine Ajax1.aspx sayfasının çıktısı gelecektir. Buradaki örnek aslında tüm web sayfası dosyaları için geçerli bir kullanım şeklidir. htm, asp, aspx, ashx, php gibi dosyalara da buradaki kod parçasında gördüğünüz şekilde talepte bulunabilir ve oluşan çıktıyı okuyabilirsiniz.

ASP.NET Sayfasına QueryString ile Talepte Bulunmak

jQuery'de ASP.NET veya sunucu taraflı çalışan başka bir sayfaya QueryString ile parametre gönderebilmek için ajax fonksiyona data isimli parametre ile değer göndermek gerekir. Burada parametreleri göndermek için farklı bir yazım biçimi bulunmaktadır. data: '{ "name":"Uğur" }' şeklinde kullanılan bu parametrede dikkat edileceği üzere değer süslü parantez karakterleri({}) içerisinde taşınmakradır. Parametre adı ve değeri çift tırnak karakteri(") içerisinde taşınmakta, eşitlik operatörü olarak da iki nokta karakteri(:) kullanılmaktadır. Eğer birden fazla parametre iletilecekse parametreler virgül karakteri(,) ile ayrılmalıdır. Aşağıda bir Ajax1.aspx isimli sayfamıza URL üzerinden parametre iletilerek ajax talebinin nasıl gerçekleştirileceği görülmektedir. Sayfada yer alan button elementimizin onclick niteliğini de aşağıdaki fonksiyonun adı ile değiştirmek gerektiğini belirtelim.

Default.aspx

function CallAspxPageWithQueryString() {

    $.ajax({

        type: 'GET',

        url: 'Ajax1.aspx',

        data: 'name=' + escape($('#text1').val()),

        success: function(result) {

            $('#sonuc').html(result);

        },

        error: function() {

            alert('Talep esnasında sorun oluştu. Yeniden deneyin');

        }

    });

}

Sayfayı çalıştırıp text1 isimli elemente bir isim girişi yapacak olursak Ajax1.aspx sayfasına ismin QueryString ile gönderildiğini ve sonucun sayfamızda yer aldığını görebiliriz.

Not: Yukarıda kullanılan escape() JavaScript fonksiyonu olası Türkçe karakter problemlerini gidermek amacıyla kullanılmıştır.


Resim: QueryString ile sayfaya gönderilen AJAX talebinin sonucu alındı

Web Servisine Talepte Bulunmak

jQuery ile XML Web Servislerine yapılan talepler JSON formatında elde edilerek değerlendirilebilmektedir. Web servislerine yapılan talep normal bir sayfa talebiyle benzerdir. Web servislerine yapılan talebin POST metoduyla gerçekleşmesi, contentType, dataType ve data parametrelerinin ajax fonksiyonunda belirtilmesi gerekmektedir. Çağrılan WebMethod parametre almıyorsa dahi data parametresi boş olarak verilmelidir. Aşağıda yerel makinede(localhost) yer alan ve name ile surname parametrelerini alan bir web servisine talep gerçekleşmektedir. Web servisindeki HellojQuery isimli metot web servis URL'sinin sonuna yazılmıştır.

NOT: Çağrılacak web servis class'ının System.Web.Script.Services.ScriptService niteliğini(attribute) uygulaması gerekmektedir. Böylece servis nesnesi kendisine gelecek asenkron taleplere cevap verebilecektir.

Default.aspx

function CallWebservice() {

    $.ajax({

        type: 'POST',

        url: 'http://localhost:5199/JQueryWebService/Service.asmx/HellojQuery',

        data: '{ "name":"Uğur","surname":"Umutluoğlu" }',

        contentType: 'application/json; charset=utf-8',

        dataType: 'json',

        success: function(result) {

            $('#sonuc').html(result.d);

        },

        error: function() {

            alert('Talep esnasında sorun oluştu. Yeniden deneyin');

        }

    });

}

Görüldüğü gibi web servisine yapılan talep JSON formatında okunmakta ve gelen değerler sayfaya aktarılmaktadır. Eğer web servisi parametre almıyor olsaydı data parametresi; data: '{}' şeklinde boş değer ile yazılmalıydı.

PageMethod'a Talepte Bulunmak

ASP.NET AJAX uygulamalarında sayfa içerisine yazdığımız WebMethod niteliği uygulamış static metotları asenkron talepler ile çağırabilmekteyiz. Bu tip metotlara PageMethod adı verilmekte ve ScriptManager ile istemciye yüklenen JavaScript fonksiyonları sayesinde bu metotlar AJAX taleplerinde kullanılabilmektedir. Benzer şekilde bu metotları jQuery ile yapılan AJAX taleplerinde de kolayca kullanabilmekteyiz. İlk olarak sayfamızın code-behind dosyasına bir tane PageMethod ekliyelim ve jQuery ile bu metodu nasıl çağıracağımızı görelim.

Default.aspx.cs

public partial class _Default : System.Web.UI.Page

{

    [System.Web.Services.WebMethod]

    public static string jQueryPageMethod(string name)

    {

        return "<h3>jQuery - PageMethod Talebi</h3>Merhaba " + name;

    }

}

PageMethod'lar ile ilgili daha detaylı bilgi almak daha önceden yazdığım bu yazıyı inceleyebilirsiniz. Peki yazdığımız bu metodu nasıl çağıracağız? Aşağıda jQueryPageMethod isimli metodun ajax fonksiyonundan nasıl çağrıldığı görülmektedir. Çağırım şekli web servisi çağırımına oldukça benzemektedir.

function CallPageMethod() {

    $.ajax({

        type: 'POST',

        url: 'Default.aspx/jQueryPageMethod',

        data: '{ "name":"Uğur" }',

        contentType: 'application/json; charset=utf-8',

        dataType: 'json',

        success: function(result) {

            $('#sonuc').html(result.d);

        },

        error: function() {

            alert('Talep esnasında sorun oluştu. Yeniden deneyin');

        }

    });

}

Bu şekilde bir ASP.NET uygulamasında farklı bileşenlere jQuery ile nasıl AJAX talepleri gönderebileceğimizi incelemiş olduk.

17 Aralık 2009 Perşembe 08:53

Yorum - RSSYorumlar (9)


facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Cache Nesnesine Erişimde Kullanılabilecek Design Pattern’ler

Caching(önbellekleme) web uygulamalarında kullanılan en önemli performans iyileştirme yöntemidir. Normal şartlarda üretilmesi gerek zaman, gerekse kaynak tüketimi açısından maliyetli olan sayfa ve nesneler caching mekanizması ile sunucunun belleğinde(RAM) saklanabilmektedir. Böylece çok hızlı erişilebilir bir nesne elde edilerek uygulamadaki sayfaların çok daha hızlı çalışması sağlanabilmektedir. Data caching yapılırken kullanılan Cache nesnesinin kullanımı pratikte oldukça basittir. Ancak Cache nesnesinin daha düzgün çalışabilir olması uygulamaların sağlığı açısından da önemlidir. Zira hiç hesapta olmayan durumlarda karşılaşılacak hatalar nedeniyle kullanıcılara hata sayfası görüntülemek zorunda kalabilir veya Cache nesnesini gereksiz yere tekrar üreterek performans kayıplarına yol açabilirsiniz. Bu yazımızda Cache nesnesinin kullanımında önemli olan iki farklı tasarım deseninin(design pattern) uygulanışını ve bu yöntemlerin faydalarını inceleyeceğiz.

Klasik Yöntem: Singleton Pattern Kullanımı

Öncelikli olarak Cache nesnesinin normal kullanımına ve bu kullanımda ne gibi bir sıkıntı olduğuna bakalım. Aşağıdaki kod örneklerinde veritabanından getirilen kayıtlar Cache nesnesinde saklanmaktadır. Cache nesnesi belirli aralıklarla doldurulmalı ve zaman aşımına uğradığında içeriği yenilenmelidir. Dolayısıyla aşağıdakine benzer bir if kontrolü akışın düzgün gitmesini sağlar. Aslında burada kullandığımız kodlama biçimi Singleton Pattern olarakta bilinmektedir.

Not: Örnekteki kodlar CacheHelper adındaki yardımcı bir sınıf içerisinde yer almaktadır.

CacheHelper.cs

public static class CacheHelper

{

    static Cache cache;

 

    static CacheHelper()

    {

        cache = HttpContext.Current.Cache;

    }

 

    public static DataTable GetProductsSingletonPattern()

    {

        //Cache nesnesinin daha önceden referansının oluşup oluşmadığı kontrol edilir

        if (cache["Products"] == null) //Cache nesnesinin içeriği ilk kez burada okunur

        {

            SqlConnection con = new SqlConnection("data source=localhost; database=Northwind; integrated security=true");

            SqlDataAdapter da = new SqlDataAdapter("Select * From Products", con);

            DataTable dt = new DataTable();

            da.Fill(dt);

            cache.Insert("Products", dt, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration);

        }

 

        return (DataTable)cache["Products"]; //Cache nesnesinin içeriği ikinci kez burada okunur

    }

}

Görüldüğü gibi öncelikli olarak Cache nesnesinin içeriğinin null olup olmadığı kontrol edilmektedir. Cache null ise veritabanına bağlanarak gerekli veriler alınmakta ve Cache nesnesine aktarılıp metottan geri döndürülmekte, null değilse doğrudan Cache içeriği metottan geri döndürülmektedir. Yazılış ve işleyiş açısından düzgün bir kod yazımı gibi görünse de burada ufak bir ayrıntı var.

Daha Etkili Bir Yöntem: State Bag Access Pattern Kullanımı

Yukarıdaki kod parçasında if koşulunun bulunduğu satıra gelindiğinde Cache null değilse, ama return ifadesinin yer aldığı satıra gelindiğinde Cache bellekten silinmiş ve null gelmişse ne olur? "Yok, o kadar da olmaz diyebilirsiniz", haklısınız. Çünkü iki satır arasındaki geçiş belki de birkaç milisaniye olacaktır. Ancak bu durumunda gerçekleşmesi az da olsa ihtimal dahilindedir. Eğer geliştirdiğiniz projede bu tip bir sorun nedeniyle kullanıcılara hata sayfası görüntülemeniz sıkıntı doğuracaksa burada yapılacak birkaç değişikle kodunuzu güzel şekilde optimize edebilirsiniz. Yapılacak iş ise metot içerisinde ilk olarak Cache nesnesinin değerini bir değişkene aktarmak ve değişken üzerinden gerekli işlemleri gerçekleştirmektir. Buradaki kod yazım biçimi literatürde State Bag Access Pattern olarak bilinmekte ve sadece Cache nesnesi değil, Session, Application gibi diğer durum yönetimi nesnelerinde de kullanmasında fayda olan bir tasarım desenidir.

CacheHelper.cs

public static DataTable GetProductsStateBagAccessPattern()

{

    //Bu desende Cache nesnesi sadece bir kez okunur

    DataTable dt = cache["Products"] as DataTable;

    if (dt == null)

    {

        SqlConnection con = new SqlConnection("data source=localhost; database=Northwind; integrated security=true");

        SqlDataAdapter da = new SqlDataAdapter("Select * From Products", con);

        dt = new DataTable();

        da.Fill(dt);

        cache.Insert("Products", dt, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration);

    }

 

    return dt;

}

State Bag Access Pattern'de durum(state) bilgisine erişim doğrudan nesne üzerinden değil, nesnenin referansını taşıyan bir başka nesne tarafından sağlanır. Yani erişimi sadece bir kez yapılarak nesne referansı farklı bir nesne ile ilişkilendirilir. Bu noktadan sonra Cache bellekten kaldırılsa dahi yeni nesne hala eski Cache'in bellekteki referansını işaretliyor olacaktır ve uygulama içerisinde istenilen veriye ulaşılabilecektir. Geliştirdiğiniz web uygulamalarında Cache nesnesine erişimi bu tasarım kalıbını kullanarak sağlamanızı tavsiye ederim(Best practices).

Cache Nesnesinin Gereksiz Yere Tekrar Üretilmesini Engellemek: Thread Safety Singleton Pattern

Şu ana kadar Cache nesnesine erişimi Singleton Pattern(klasik yöntem) ve State Bag Access Pattern ile nasıl yapıldığını gördük. Cache nesnesine erişimle ilgili bir diğer önemli durumda Cache nesnesine aynı anda birden fazla thread tarafından erişimin olma ihtimalidir. Sonuçta Cache'de genellikle üretilmesi zaman alan nesneleri saklarız. Örneğin veritabanından 10 saniyede sürede çektiğimiz bir veriyi Cache'e atıyoruz. Saat 12:00:00, 12:00:04 ve 12:00:08 gibi üç ayrı talebin geldiği bir durumda veritabanına 3 kez gidilir. Ancak bizim burada istediğimiz sadece ilk talepte veritabanına gidilmesi olacaktır. Bu durumu şekilde şöyle izah edebiliriz:


Şekil: Cache nesnesine gelen taleplerin sırası ve normal işleyişi

Görüldüğü gibi veritabanında 10 saniye sürecek bir işlem esnasında Cache nesnesine 10 saniyelik sürede gelen tüm talepler veritabanına gönderilecektir. Zira ilk talep sonrasında Cache nesnesi ancak 10 saniye sonra doldurulacaktır. Yani sonrasında gelen iki talepte Cache hala null görüneceği için bu işlemler yapılır. Halbuki caching'deki temel mantık veritabanında yoğunluğa yol açan sorguların azaltılması ve sadece belirli aralıklarla veritabanına gidilmesiydi. Burada performans açısından bizi veritabanına daha az götürecek bir yapı sağlıklı olacaktır. Singleton pattern'in Thread Safety yöntemi olarak bilinen nesne kilitlemesi işlemi burada belirttiğimiz sorunun giderilmesi için çözümümüz olacaktır. Thread Safety Singleton Pattern'de aynı anda birden fazla thread'in erişmesinin istenilmediği nesne bir lock bloğu ile kilitlenir. Bu kilitleme esnasında nesneye gelen diğer talepler bekletilecektir. Kitleme bittiğinde ise diğer threadler lock bloğuna giriş yapar ve gerekli işlemleri yaparlar. Dolayısıyla yukarıda şekilde anlattığımız senaryodaki 2. ve 3. talepler veritabanına gitmek yerine 1. talebin bitmesini bekleyecektir. Bu bekleme sonunda uygun if koşulunu yazarak veritabanına atılacak gereksiz sorguları engeleyebiliriz. Aşağıdaki kodlarda bu tasarım deseninin uygulanışı yer almaktadır.

CacheHelper.cs

public static class CacheHelper

{

    static Cache cache;

    static object obj;

 

    static CacheHelper()

    {

        cache = HttpContext.Current.Cache;

        obj = new object();

    }

 

    public static DataTable GetProductsThreadSafe()

    {

        //Bu desende ise Cache nesnesine eş zamanlı gelecek birden fazla talepten sadece ilk gelen talep

        //Cache nesnesini olusturacaktir

        if (cache["Products"] == null)

        {

            lock (obj) //Burada obj nesnesi kilitlenerek farklı thread'lerin blok içerisine erişimi engellenmektedir

            {

                if (cache["Products"] == null)

                {

                    SqlConnection con = new SqlConnection("data source=localhost; database=Northwind; integrated security=true");

                    SqlDataAdapter da = new SqlDataAdapter(" Select * From Products", con);

                    DataTable dt = new DataTable();

                    da.Fill(dt);

                    cache.Insert("Products", dt, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration);

                }

            }

        }

 

        return (DataTable)cache["Products"];

    }

}

lock bloğunda kullanılan obj isimli nesne class içerisine bir field olarak tanımlanmıştır. Burada Cache'in null gelmesi durumunda lock bloğuna girilecek ve obj nesnesi farklı talepler tarafından kullanılamayacaktır. Ne zaman ki lock bloğu dışına çıkılacak, bu andan sonra gelen talepler bu bloğa girebilecektir. Burada dikkat çeken noktalardan birisi Cache nesnesinin null olup olmama durumunun iki kez kontrol edilmesidir. Birinci if koşulu Cache'in null olmadığı durumlarda gereksiz nesne kilitlemesini engellemek, ikinci if ifadesi ise birinci talepten sonra gelen ve lock bloğunda bekleyen taleplerin Cache'i gereksiz yere tekrar oluşturulmasını engellemek için gereklidir. lock bloğuna giren ikinci, üçüncü ve sonraki talepler içerideki koşulda Cache'in null olmadığını görecek ve if blokları dışına çıkılarak Cache nesnesi okunacaktır. Aşağıdaki şekilde Thread Safe Singleton Pattern uygulanışında gelen taleplerin işleyişi görülmektedir.


Şekil: Thread Safe Singleton Pattern'in uygulanmasında ardarda gelen 3 talebin işleyişi

Bu yazımda Cache nesnesine erişim sağlarken kullanılan klasik yöntemi ve bu klasik yöntemin yol açabileceği sorunların nasıl giderilebileceğini inceledik. Cache nesnesine erişimde klasik Singleton Pattern'in uygulanmasından ziyade yukarıda incelediğimiz State Bag Access Pattern veya Thread Safe Singleton Pattern'in uygulanmasının daha faydalı olduğunu gördük. Şunu unutmamak gerekir ki bellek(RAM) uygulamalarda en hızlı ve en kolay erişebileceğimiz sistem kaynağımızdır. Eğer sunucuda yeterli miktarda bellek varsa(ki günümüzde uygulama sunucularının bellekleri çok büyük miktarlarda olabilmekte) işleyişi yavaşlatacak birçok işlemin çıktısının Cache'de saklayabiliriz. Tabi ki Cache'e erişimde de en kullanışlı ve sağlıklı yöntemi belirlemekte fayda olacaktır.

25 Kasım 2009 Çarşamba 21:48

Yorum - RSSYorumlar (4)

Kategori: ASP.NET

facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Authorization Ayarları ve CSS, JavaScript Gibi Dosyalara Özel Hak Tanıma

ASP.NET uygulamalarında Authorization mekanizmasını kullanarak kullanıcı tabanlı ve rol tabanlı güvenlik işlemlerini kolay şekilde gerçekleştirebiliyoruz. Uygulamanın tamamı veya belirli bir dizini web.config dosyası içerisindeki <authorization> elementinde belirlenecek kurallara göre güvenli şekilde erişilebilir hale getirilebilir. Tabi ki bu kadar kolay işin içerisinde bazen ufak tefek sorunlarla da karşılaşabiliyoruz. Bu yazımda geçmişte karşılaştığım ve çözümünün sizler için faydalı olabileceğini düşündüğüm bir ipucundan bahsetmek istiyorum.

Geliştirdiğiniz uygulamadaki tüm sayfalara(veya belirli bir dizindeki tüm sayfalara da olabilir) sadece sisteme giriş yapmış kullanıcılar erişsin istiyorsunuz. Dolayısıyla kullanıcılara login sayfanız dışındaki tüm sayfaları kapatmanız gerekiyor. Burada sorun yok; zira ASP.NET authentication ve authorization için gerekli ayarları yapmanız durumunda Login.aspx dışındaki tüm dosyaları kullanıma kapatacaktır. Dikkat! Tüm sayfalar değil, tüm dosyalar erişime kapatılacaktır dedim. Yani, eğer login sayfasında CSS, skin gibi stillendirme dosyaları, JavaScript, resim vb. dosyaları kullanırsanız, bu dosyalar da authorization ayarlarına takılacak ve kullanıcıya gönderilmeyecektir. Bu nedenle login sayfanıza erişen kullanıcı bu sayfayı sizin tasarladığınız gibi göremeyecektir. Eğer login sayfasının düzgün şekilde görüntülenmesini istiyorsanız, bu sayfa içerisinde kullandığınız diğer dosyalar için farklı authorization ayarları yapmanız gerekecek.

Önce sorunumuzu somutlaştıralım. Basit bir Login.aspx sayfası tasarlıyorum ve web.config dosyasında authorization ayarlarını tüm sayfalara sadece sisteme giriş yapmış kullanıcıların erişebileceği şekilde değiştiriyorum. style.css adında bir de stil dosyamız var. Login.aspx ve style.css dosyalarının içeriği bizim için önemli değil. Önemli olan web.config;

web.config

<authentication mode="Forms"/>

<authorization>

    <deny users="?"/>

</authorization>

...


Bu değişikliklerden sonra sisteme giriş yapmamış olan kullanıcılar Login.aspx dışındaki hiçbir dosyaya erişemeyecektir. Eğer Login.aspx içerisinde style.css gibi bir dosyaya bağlantı kurulmuş ise bu dosya da erişilemez olacaktır. Dolayısıyla Login.aspx çalıştırıldığında istemciye style.css dosyası gönderilmeyeceği için sayfa görsel olarak istenildiği gibi görüntülenmez. Aşağıda zemin rengi mavi, font tipi de Arial olarak belirlenmiş olan sayfamızın bu ayarlardan sonraki görünümü bulunmakta.

Görüldüğü gibi ne zemin rengi mavi, ne de fontlar Arial :) Peki çözüm nedir? Bu tip durumlarda Login sayfanızın içerdiği tüm CSS, JavaScript ve resim dosyalarının düzgün şekilde istemciye gönderilmesini istiyorsanız web.config içerisinde bu dosyalar için özel hak tanımlaması yapmanız gerekiyor. web.config dosyasının en üst seviyesinde açacağınız <location> elementi ile dosyaları tanımlamalı ve bu dosyalara tüm kullanıcıların erişmesini sağlamalısınız. <location> elementi <configuration> elementinin içerisinde olmalı ve <configSections> tanımlamalarının altında yer almalıdır.

web.config
<configuration>

    <configSections>

        ...

    <location path="styles/style.css">

        <system.web>

            <authorization>

                <allow users="*"/>

            </authorization>

        </system.web>

    </location>

style.css dosyası sisteme giriş yapmamış kullanıcılar tarafından da görülebilir hale geldi. Buradaki allow veya deny elementleri içerisinde kullanılan * karakteri tüm kullanıcılar, ? karekteri ise sisteme giriş yapmamış(anonim) kullanıcılar anlamına gelmektedir. Şimdi Login.aspx sayfasına tekrar bakıyoruz.

style.css dosyası istemciye gönderildiği için artık stiller sayfaya uygulanabilecektir. Umarım faydalı bir ipucu olmuştur.

22 Ekim 2009 Perşembe 00:55

Yorum - RSSYorumlar (0)

Kategori: ASP.NET

facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

AutoCompleteExtender ile Google Suggest Görünümünde Otomatik Tamamlayıcı Hazırlamak

AJAX'ın artık çoğu web uygulamasında kullanılır hale gelmesi web sitelerindeki standartları da arttırdı. Artık çoğu sitenin kendi arama motorunda ziyaretçilere yardımcı olması için otomatik tamamlayıcı metinleri kullandığını görebiliyoruz. Yani siz gidip arama kutusuna birkaç harf yazdığınızda arka planda sunucuya iletilen bir taleple size yardımcı olabilecek arama kriterleri bulunuyor ve size dinamik açılan bir kutuda seçenek olarak sunuluyor. Bu yazımda ASP.NET uygulamalarında kullanabileceğimiz Control Toolkit kütüphanesinde yer alan otomatik metin tamamlayıcı kontrolümüz olan AutoCompleteExtender kontrolünü nasıl özelleştirebileceğimizi inceleyeceğiz. Bu incelemenin sonunda da AutoCompleteExtender kontrolünü aslında Google Suggest görünümünde çalıştırabileceğiz.

AutoCompleteExtender kontrolü adından da anlaşılacağı üzere bir extender; yani ancak başka bir kontrole bağlanarak çalıştırılabilir. TextBox ile birlikte kullanılan bu kontrol kullanıcının TextBox içerisinde belirli sayıda karakter yazması ile tetiklenmeye başlar ve bu noktadan sonra her harf değişiminde tetiklenmeye devam eder. Böylece kullanıcının yazdığı her ifadeyi yakalayabilir ve bu ifadeler ile sunucuya AJAX talepleri yollayabilir ve gelen sonuçları da otomatik olarak açılan bir pencereden kullanıcıya sunabilirsiniz. Aslında AJAX'ın web sayfalarına uygulanışının en güzel örneklerinden birisi. Bu kontrol sunucuda yapacağı talepleri bir web servisi üzerinden gerçekleştirir. Çağrılacak olan web servisine kullanıcının yazdığı ifade yollanır ve sunucuda veritabanı vb. bir kaynaktan istenilen kayıtlar çekilerek TextBox'ın hemen alt kısmında seçilebilir öğeler olarak listelenir. AutoComplete kontrolü hakkında fazlasıyla hikaye anlattım sanırım. Gelin şimdi bu işi nasıl yaparız, bir inceleyelim.

Web servisi ile işe başlayalım. Amaç basit olarak işe yarar metinler döndürmek olduğu için sade bir metot hazırladım. AutoCompleteExtender TextBox'a girilen değeri web metoduna prefixText parametresi olarak göndererektir.

MusicService.asmx

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.Web.Script.Services.ScriptService]

public class MusicService : System.Web.Services.WebService

{

    [WebMethod]

    public string[] GetSong(string prefixText, int count)

    {

        string[] songList1 = new string[] { "sevme|3", "seviyorum|7", "sevdim sevilmedim|5" };

        string[] songList2 = new string[] { "zaman|7", "zaten|2" };

 

        if (prefixText == "se")

            return songList1;

        else if (prefixText == "za")

            return songList2;

        else

            return null;

    }

}

Bu örnek için basit bir web servisi hazırlıyorum. Gerçek senaryolarda bu metot içerisinden veritabanındaki ilgili tablonuza uygun LIKE koşullarıyla SELECT cümleleri göndermeniz ve elde ettiğiniz sonuç kümesini yine string dizisine eklemeniz yeterli olacaktır. Oluşturduğumuz string dizisi içerisinde yer alan değerlere bakacak olursanız ilk bakışta enteresan gelecek bir format dikkatinizi çekecektir. Örneğin; zaman|7 Buradaki | işareti aslında bizim için ayıraç görevi üstlenecek. Aslında makalenin başlığına bakacak olursanız amacımız AutoCompleteExtender'ı özelleştirerek Google Suggest tarzında görüntülemek. Dolayısıyla bir metin ve o metinden kaç tane sonuç geleceğini getirmemiz gerekiyor. | simgesinin solunda tamamlayıcı metnimiz, sağında ise o metinden toplam kaç tane sonuç bulduğumuz yer alıyor(zaman kelimesinden toplam 7 tane sonuç var gibi)

Gelelim AutoCompleteExtender kontrolünü hazırlamaya. Bir ASP.NET sayfasına ToolkitScriptManager ve TextBox kontrolü ekliyoruz. TextBox'ın sağ üst köşesindeki smart-tag ikonundan Add Extender diyerek AutoCompleteExtender'ın textbox'a bağlanmasını sağlayabiliriz. Aslında sadece bağlama işlemi yeterli olmayacak bu örnek için, ek olarak birkaç tanımlama daha yapmamız gerekecek. Aşağıdaki kodlar basit şekilde web servisini çağırabilmenizi sağlayacaktır. Bunların dışında bu örnek için gerekli JavaScript tanımlamaları da aşağıda yer alıyor.

Default.aspx

<form id="form1" runat="server">

    <cc1:ToolkitScriptManager ID="scriptManager1" runat="server">

    </cc1:ToolkitScriptManager>

 

    <asp:TextBox ID="txtSearch" runat="server"></asp:TextBox>

    <cc1:AutoCompleteExtender ID="txtSearch_AutoCompleteExtender" runat="server"

       DelimiterCharacters="" MinimumPrefixLength="2" Enabled="True" TargetControlID="txtSearch"

       BehaviorID="AutoCompleteEx" ServicePath="MusicService.asmx" ServiceMethod="GetSong"

       OnClientPopulated="onClientPopulated" OnClientItemSelected="onItemSelected">

    </cc1:AutoCompleteExtender>

</form>

AutoCompleteExtender kontrolünün tanımlamasının son iki satırında yer alan nitelikler anlattığımız örnek için olmazsa olmaz değerleri saklamakta. Bu niteliklerin görevlerine ve nerelerle bağlantılı olduklarına kısaca değinelim:

- ServicePath: Bağlanacağımız web servisinin adresi
- ServiceMethod: Web servisi içerisinde yer alan ve string dizisi olarak bize sonuç listesini döndürecek web metodu.
- BehaviorID: JavaScript fonksiyonları içerisindeyken AutoCompleteExtender kontrolünün içeriğindeki listeye erişmemizi sağlayan özellik
- OnClientPopulated: İstemci tarafında web servisinden gelen veriler yüklendiği esnada tetiklenen olay fonksiyonu. Az sonra hazırlayacağımız onClientPopulated isimli JavaScript fonksiyonunda servisten gelen değerlerin istenilen formatta listede yer almasını sağlayacağız.
- OnClientItemSelected: Tamamlama listesinden bir seçenek seçildiği esnada istemci tarafında tetiklenecek olay fonksiyonu. Bu JavaScript fonksiyonda ise seçilen seçeneğin düzgün şekilde textbox'a aktarılmasını sağlayacağız.

Web servisinden gelen veriler "zaman|7" gibi bir formatta olduğu için eğer istemci tarafında bu verilerin üzerinde oynama yapılmazsa aynen textbox içerisinde görüntülenir. Bu verilerin değiştirilebilmesi için istemci tarafında servis verilerini değiştirme işini yapacak bir JavaScript fonksiyonuna ihtiyacımız olacak(onClientPopulated). AutoComplete kontrolü seçim yapıldığında kendi koleksiyonunda hala servisten gelen orjinal veriyi saklamaktadır. Bu nedenle kullanıcı bir değeri seçtiğinde textbox'a zaman|7 yerine zaman yazabilmemiz için bir de listeden seçim işlemini istemci tarafında yakalamamız ve seçilen değeri düzgün şekilde textbox'a aktarmamız gerekecektir. Bu işlemi de onItemSelected isimli JavaScript fonksiyonunda yapacağız. AutoCompleteExtender'ı Google arama motorunun kullandığı formata benzetebilmek için iki tane CSS class'ı tanımlıyoruz(bu da autocomplete kutusunun daha şık görüntülenmesi için). Css class'ları ve JavaScript fonksiyonlarını aşağıda bulabilirsiniz. JavaScript fonksiyonları ile ilgili detaylı açıklamaları yorum satırlarında bulabilirsiniz.

Default.aspx

<head runat="server">

    <title>AutoComplete Örneği</title>

    <style type="text/css">

        .metin{ float:left; font-family:Arial; font-size:11px }

        .bulunan_kayit{ float:right; color:Green; font-family:Arial; font-size:11px }

    </style>

 

    <script type="text/javascript">

        //Listenin client tarafında yüklenmesi esnasında tetiklenen fonksiyon.

        //Listeye eklenecek olan "zaman|7" şeklindeki metnin "zaman   7 sonuç" şeklinde listelenmesini sağlar.

        function onClientPopulated(sender, e) {

            var completionList = $find("AutoCompleteEx").get_completionList(); //Orhinal liste alınır

 

            //Orjinal listedeki her item istenilen formata getirilir

            for (i = 0; i < completionList.childNodes.length; i++) {

                var okunanDeger = completionList.childNodes[i]._value.split('|');

                var metin = okunanDeger[0];

                var kayitSayisi = okunanDeger[1];

                completionList.childNodes[i].innerHTML = '<span class="metin">' + metin + '</span><span class="bulunan_kayit">' + kayitSayisi + ' sonuç</span>';

            }

        }

 

        //Menüden seçim yapıldığında tetiklenen fonksiyon.

        //Seçilen index değerine göre AutoCompleteList'te yer alan değer bulunur.

        //Bu index'te yer alan değer alınarak | simgesinin solundaki bilgi(metin) textbox'a aktarılır.

        function onItemSelected(e) {

            var index = $find("AutoCompleteEx")._selectIndex;

            var deger = $find("AutoCompleteEx").get_completionList().childNodes[index]._value;

            var okunanDeger = deger.split('|');

            var metin = okunanDeger[0];

 

            document.forms[0]['txtSearch'].value = metin;

        }

    </script>

</head>

AutoCompleteExtender kontrolünde sadece görünüm olarak değişiklik yapmak isterseniz CSS class tanımlamaları yapmak ve bu tanımlamaları CompletionListCssClass, CompletionListHighlightedItemCssClass gibi özelliklere bağlamak yeterlidir. Ancak bu örneğimizde gerek web servisinden gelen verinin ayrıştırılarak farklı şekillerde görüntülenmesi, gerekse AutoCompleteList üzerinden seçilen seçeneğin düzgün şekilde textbox'a aktarılması gerektiği için sadece CSS class'ları işimizi görmez, JavaScript fonksiyonları ile AutoCompleteExtender içeriğinin doldurulması ve üzerinde seçenek seçme işlemlerini de ele almamız gerekir. Gerekli kodları hazırladıktan sonra artık AutoCompleteExtender kontrolünün çalışmasını test edebiliriz.

AutoCompleteExtender'ın Görünümü
Resim: AutoCompleteExtender kontrolümüz Google Suggest’i taklit ederken :)

Resimden de anlaşılacağı gibi Google Suggest'e oldukça benzer bir görünüm elde edebilmişiz.

25 Ağustos 2009 Salı 21:23

Yorum - RSSYorumlar (1)

Kategori: ASP.NET

facebook'da Paylaş   twitter'da Paylaş   friendfeed'de Paylaş   del.icio.us'da Paylaş   stumpleupon'da Paylaş   Permalink

Bağlantılar



Takip Et

RSS Feed twitter friendfeed

Seminer/Webiner Programım

  • Seminer-WebinerASP.NET 4.0 WebForms Yenilikleri (Microsoft İstanbul Ofisi)
    29 Mayıs 2010

  • Seminer-WebinerVisual Studio 2010 Yenilikleri (Osmangazi Üniversitesi)
    15 Mayıs 2010

  • Seminer-WebinerASP.NET AJAX ile Zengin Internet Uygulamaları Geliştirme (Microsoft İstanbul Ofisi)
    3 Mayıs 2010

>> Etkinlik Takvimi