F5 dergisi
Transcript of F5 dergisi
SAYI: 3
F5 DERGİ © KASIM-ARALIK 2012
{ YAZILIM }
{ PROGRAMCILIK }
{ WEB }
# Ücretsiz abonelik: http://f5dergi.com
Entity Framework
ASP.NET MVC Özelleştirilmiş
Model Bağlama
Ücr
etsi
zdir
JavaScript ile
nesne-yönelimli
programlama
Duyarlı Web
Tasarımları ve
CSS Medya Sorguları
HTML5 ile çevrim
dışı çalışabilen
web uygulamaları
Birim Testler
TÜRKÇE YAZILIM VE PROGRAMCILIK DERGİSİ
2
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
F5 Dergi’yi
Twitter’da takip et
@F5Dergi
Microsoft SQL Server 2012 Service Pack 1 http://
t.co/446WfpMX… ilgilenenlere...
Microsoft'ta Windows bölüm başkanı Steven Sinofsky istifa
etti. Windows 8 için pek iyi bir haber olmasa gerek http://
t.co/8vr4ZdH0
Skype 6.0 Windows ve Mac masaüstü bilgisayarlar için:
http://t.co/7oefxaGc…. Facebook ve Microsoft hesaplarıyla
entegrasyon büyük kolaylık..
Windows Azure Web Siteleri şimdi ASP.NET 4.5'i de destekli-
yor... Çok yaşa BULUT...
iPad Mini: http://t.co/ah3oi1Dl
Apple iPad Mini'yi anons etti: 20cm ekran, 7.22mm kalınlı-
ğında 1024 x 768 px çözünürlükte, 310 gram ağırlığında.
DualCoreA5 çip, 10saat pil
Android telefon sahipleri, telefonunuzun güvenliği için lo-
okout.com uygulamasını kurmanız tavsiye ederiz.
Microsoft'tan XBox Music xbox.com/en-US/Music, Apple'ın
iTunes'una rakip olacak gibi..
Microsoft'tan bedava e-kitaplar: http://t.co/glsILLfd
Microsoft "Windows Phone Yazılım Merkezi" açılışını anons
etti http://t.co/qYuqkVgY
Yazılımcılar için Office 2013 ve yeni "app" modeli video eği-
tim seti
http://t.co/x0Lgh0Lx
3
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
F5 Dergi Haziran - Temmuz 2012
İÇİNDEKİLER
Bu sefer biraz gecikmeli de olsa üçüncü
ve 2012’deki son sayımızla yeniden
merhaba herkese!
Bir önceki sayımızda da belirttiğimiz gibi
yazılım sektörü için yoğun dönemlerden
geçtiğimiz bu aylarda F5 Dergi ekibi ola-
rak son teknoloji ve ürünleri sizler için
mercek altına almaya gayret gösteriyo-
ruz. Kısıtlı imkanlarla yürütmekte oldu-
ğumuz gönüllü çalışmalarla sizlere fay-
dalı olacak bir içerik hazırlamak başlıca
amacımız. Web sitemiz http://f5dergi.com
artık Windows Azure platformunda ça-
lışmakta. Gelecek sayılarımızda yer ala-
cak Windows Azure bulut teknolojileriyle
ilgili makalelerin planlarını yapmaya
başladık bile.
İnanıyoruz ki günümüzde önemi giderek
artan iki konu var: Bulut teknolojileri ve
eklentisiz web, yani HTML5/JavaScript/
CSS3. Bu konularda meslektaşlarımız-
dan görüş ve deneyimlerini F5 Dergi
aracılığıyla paylaşmalarını rica ediyoruz.
Bu sayımızda çevrim dışı çalışabilen
HTML5 uygulamaları, CSS3 medya
sorguları ile duyarlı web tasarımları
ve JavaScript ile nesne yönelimli
programlama gibi konuları ele almamı-
zın sebebi bu konuların eklentisiz web
dalgasıyla büyük önem kazanıyor olma-
sıdır.
Microsoft’un HTML5, JavaScript ve web
standartlarını kucaklayıp Windows 8,
Office 2013 ve SharePoint 2013 ile bü-
tün web yazılımcılarını bu teknolojiler
için Office Store ve Windows Store ara-
cılığıyla son kullanıcı ile buluşacak yazı-
lımlar üretmeye davet etmesi sektörde
not edilmesi gereken gelişmelerdendir.
Microsoft tarafından Temmuz 2012 itiba-
riyle açık-kaynak haline dönüştürülen ve
yazılımcı toplumun da katkılarıyla her
geçen gün gelişmekte olan Entity Fra-
mework konusunu incelediğimiz maka-
lemizi de yararlı bulacağınızı ümit ediyo-
ruz.
Bazı okuyucularımızdan gelen talepler
üzerine de yazılım projelerinin birim test-
lerle desteklenmesi konusunu da işledi-
ğimiz bu sayımızla okuyucularımıza şim-
diden mutlu seneler diliyoruz. Yeni yılda
yeniden görüşmek üzere, hoşça kalın.
Yayıncı ve Baş Yazar
Özgür Özgüven
İÇİNDEKİLER
4
ASP.NET MVC
Özelleştirilmiş Model Bağlama
11
JavaScript ile
Nesne Yönelimli
Programlama
22
CSS Medya Sorguları ve Du-
yarlı Web Tasarımı
39
Entity Framework
56
HTML5
Çevrim dışı (offline)
çalışabilen web uygulamaları
63
Birim Testler
F5 DERGİ
4
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ASP.NET MVC Özelleştirilmiş Model Bağlama
Önceki sayılarımızda incelemeye
başladığımız ASP.NET MVC’ye bu
makalemizde otomatik olarak ger-
çekleştirilen “model bağlama” kavra-
mının taleplerimizi karşılayamadığı
durumlarda başvurabileceğimiz
özelleştirilmiş model bağlama
(custom model binding) tekniklerini
inceleyerek devam ediyoruz.
Model bağlama kavramının
ASP.NET MVC’nin HTTP GET ve/veya HTTP POST talepleri ile sunucuya
ulaşan ham verileri otomatik olarak aksiyon metotlarının beklediği tipte mo-
del nesnelerine dönüştürme işlemi olduğunu önceki makalelerde detaylı bir
şekilde inceledik. Ancak ara-yüzlerimizi oluşturan HTML sayfalarımız ve
model yapılarımız karmaşıklaştıkça ASP.NET MVC tarafından gerçekleştiri-
len bu işlem yetersiz kalabilmektedir.
Bu gibi durumlarda kontrolör sınıflarımızda Request.Form veya
Request.QueryString gibi koleksiyonlardan direkt veri okuyabilir (ki bu-
nun neden tavsiye edilmeyen bir yöntem olduğunu önceki sayımızda açık-
ladık) ya da aksiyon metotlarımıza FormCollection tipinde bir parametre
ekleyerek bu koleksiyondan veri okuyabiliriz. Ancak bu yöntemler aksiyon
metotlarımızda gereksiz bir kirliliğe sebep olacaktır. Daha asil bir çözüm
yolu özelleştirilmiş model bağlayıcı sınıflar geliştirmek olacaktır.
Örnek olarak kullanmak üzere aşağıdaki veri yapısını ele alalım.
Diyagramda da görülebileceği gibi Makale ve Etiket arasındaki ilişki Maka-
leEtiket aracı tablosuyla sağlanıyor.
ASP.NET MVC Özelleştirilmiş Model Bağlama
Örnek Proje
http://f5dergi.com/indir/
Kod/omb.zip
(Üyelik gerektirmektedir)
Platform ve Teknolojiler
Visual Studio 2012,
MVC 4, Entity Framework 5
5
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Şimdi Makale modelimiz için şöyle bir kontrolör sınıfı düşünelim:
public class MakaleController : Controller { //veri tabanı bağlamı private VTEntities vt = new VTEntities(); //HTTP GET /Makale/Yönet/{id} public ActionResult Yönet(int id) {
//bütün etiketleri ViewBag'e yerleştir ViewBag.TümEtiketIDleri = vt.Etiketler.ToList(); Makale model = vt.Makaleler.Single(o => o.ID == id); return View(model); } //HTTP POST /Makale/Yönet/{id} [HttpPost] public ActionResult Yönet(Makale makale) { //makaleyi kaydet //bu kısmı sonra kodlayacağız return RedirectToAction("Yönet", new { id = makale.ID }); } }
Burada dikkat çekmek istediğimiz nokta Yönet aksiyon metodumuzda veri
tabanı bağlamımızdan okuduğumuz bütün etiketleri dynamic tipli ViewBag
yapısına eklediğimiz TümEtiketIDleri alanına atıyoruz. Daha sonra bu
bilgiyi Yönet ara-yüzümüzde bir etiketler checkbox listesi oluşturmak için
kullanacağız.
Şimdi bu ara-yüzü oluşturalım. Örneğimizde model kavramını temsil eden
Makale sınıfına ait ID, Ad, Metin ve Tarih alanlarına ek olarak bu makaleye
ait etiketlerin seçilebilmesi için ViewBag.TümEtiketIDleri alanından oku-
yarak oluşturacağımız etiketler listesi içinde ilgili makalenin Etiketler alanın-
da var olanlarını seçili olarak göstereceğiz.
Makale Yönet Ara-yüzü @using ÖzelModelBağlama.EF @model ÖzelModelBağlama.EF.Makale <!DOCTYPE html> <html> <head> <title>Yönet</title> </head> <body> @using (Html.BeginForm()) {
ASP.NET MVC Özelleştirilmiş Model Bağlama
6
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
@Html.ValidationSummary(true) <fieldset> <legend>Makale</legend> @Html.HiddenFor(model => model.ID) <div class="editor-label"> @Html.LabelFor(model => model.Ad) </div> <div class="editor-field"> @Html.EditorFor(model => model.Ad) </div> <div class="editor-label"> @Html.LabelFor(model => model.Metin) </div> <div class="editor-field"> @Html.TextAreaFor(model => model.Metin, new {style = "width: 440px"}) </div> <div class="editor-label"> @Html.LabelFor(model => model.Tarih) </div> <div class="editor-field"> @Html.EditorFor(model => model.Tarih) </div> <br /> <span>ETİKETLER</span> <br /> @{ var makaleEtiketIDleri = Model.Etiketler .Select(o=>o.ID) .ToList(); //makaleye ait EtiketIDleri foreach (Etiket etiket in ViewBag.TümEtiketIDleri) { if (makaleEtiketIDleri.Contains(etiket.ID)) { <input type="checkbox" name="EtiketID" checked /> } else { <input type="checkbox" name="EtiketID" /> } <span>@etiket.Ad</span> <br /> } } <p><input type="submit" value="Save" /></p> </fieldset> } </body> </html>
Şimdi bu ara-yüz aracılığıyla gönderilecek HTTP POST taleplerine karşılık
gelen aksiyon metodumuza dönelim. ASP.NET MVC tarafından yapılacak
ASP.NET MVC Özelleştirilmiş Model Bağlama
7
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
olan otomatik model bağlama işlemi aracılığıyla [HttpPost] ile dekore
edilmiş aksiyon metodumuza gönderilecek makale parametresini yakından
inceleyelim ve etiketlerle ilgili her hangi bir bilgi içerip içermediğini kontrol
edelim.
Aksiyon metodumuzun uygun bir noktasına bir breakpoint koyarak aşağıda-
ki gibi görüntülediğimiz zaman göreceğiz ki makale.Etiketler alanı boş
olacaktır.
ASP.NET MVC otomatik model bağlama mekanizması HTTP POST ile
gönderilen HTML alanlarını Makale sınıfına ait olan ID, Ad, Metin ve Ta-
rih gibi basit değerli alanlara problemsiz bağlayabiliyor ancak EtiketID adlı
checkbox alanlarını ICollection<Etiket> Etiketler alanına bağlaya-
mıyor.
İşte bu ve benzeri durumlarda kendi mantığımızı özelleştirilmiş model bağ-
layıcı sınıflar geliştirerek ve bu sınıfları spesifik model yapıları için Glo-
bal.asax sınıfında yer alan Application_Start metodunda kayıt ederek mo-
del bağlama işleminde çeşitli iyileştirmeler yapabiliriz.
Özelleştirilmiş model bağlayıcı sınıflar
Özel model bağlayıcı sınıf yazmanın iki yöntemi vardır. 1) IModelBinder
ara-yüzünden kalıt almak veya 2) DefaultModelBinder sınıfından kalıt al-
mak.
Bu iki işlem arasındaki fark DefaultModelBinder yapısından kalıt alarak ya-
zılan sınıflarda baz (base) sınıfta mevcut bulunan işlevlerden faydalanabile-
cek olmamızdır. IModelBinder’den kalıt alarak yazacağımız özel model bağ-
layıcılarda ise (IModelBinder bir ara-yüz olduğundan) doğal olarak böyle bir
şansımız olmayacaktır.
Biz örneğimizde mevcut model bağlama işlemine ekstra işlev kazandırmak
istiyoruz. Bu yüzden DefaultModelBinder sınıfından kalıt almak bizim için
daha akıllıca olacaktır. Yapmamız gereken DefaultModelBinder sınıfından
kalıt alan bir sınıf yazmak ve baz sınıfa ait BindModel metodunu yeni-
ASP.NET MVC Özelleştirilmiş Model Bağlama
8
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
den yazmaktır (override). Şimdi bu bilgiler ışığında aşağıdaki özel model
bağlayıcı sınıfı yakından inceleyelim.
public class MakaleBağlayıcı : DefaultModelBinder { public override object BindModel( ControllerContext controllerContext, ModelBindingContext bindingContext) { //baz sınıf tarafından sağlanan işlevden istifade ederek // kısmen bağlanmış model nesnemizi okuyoruz Makale makale = (Makale)(base.BindModel(controllerContext, bindingContext)); //şimdi Etiketler alanını dolduralım //EtiketID adlı HTML alanlardan okuma yapalım string hamVeri = bindingContext.ValueProvider.GetValue ("EtiketID").AttemptedValue; string[] etiketIDleri = hamVeri.Split(','); //veri tabanı bağlamı yarat VTEntities vt = new VTEntities(); //veri tabanından bütün etiketleri oku var bütünEtiketler = vt.Etiketler.ToList(); makale.Etiketler = new HashSet<Etiket>(); //herbir etiketID için foreach (string etiketID in etiketIDleri) { int etkID; if (int.TryParse(etiketID, out etkID)) //etiket ekle makale.Etiketler.Add(bütünEtiketler.Single(o => o.ID == etkID)); }
return makale; } }
Kod içi yorumlarla da desteklediğimiz bu sınıfta neler olup bittiğini irdeleye-
lim şimdi. Yeniden yazdığımız BindModel metoduna ait iki parametre var:
controllerContext ve bindingContext. controllerContext bize ilgili kont-
rolör nesnesi ve içinde bulunduğumuz HttpContext’e erişim imkanı verir.
Ancak bu örnekte bu parametreyi kullanmıyoruz.
bindingContext ise bize içinde bulunduğumuz model bağlama işlemi ile ilgili
bağlama erişim imkanı sağlar. Mesela bindingContext.ValueProvider
HTTP bağlamından gelecek olan değerleri sorgulamak için kullandığımız bir
yapıdır. Yukarıdaki BindModel metodu içinde bindingContext.Value
Provider.GetValue("EtiketID").AttemptedValue şeklinde HTML
ASP.NET MVC Özelleştirilmiş Model Bağlama
9
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
formdan gelecek olan değerlere ulaşıyoruz. Ara-yüzde checkbox olarak
yazdırdığımız EtiketID adlı alanlardan seçili olan değerleri hamVeri adlı
değişkene okuyoruz. Yalnız bu işlemden önce baz sınıfın BindModel meto-
dunu (base.BindModel) çağırarak kısmen doldurulmuş olan model nes-
nemizi makale değişkenimize atıyoruz. Pek tabii ki makale nesnemize ait
diğer alanları da base.BindModel metodunu çağırarak değil bindingCon-
text.ValueProvider ifadesinden direkt okuma yaparak da doldurabilirdik.
Daha sonra elimizde bulunan EtiketID değerlerini kullanarak veri tabanın-
dan asıl etiket değerlerini okuyup makale nesnemizi ilgili Etiket nesneleriyle
dolduruyoruz ve model nesnemizi bu şekilde döndürüyoruz.
Model bağlayıcı sınıfların kayıt edilmesi
Makale modeli için özelleştirdiğimiz model bağlayıcımız hazır... Şimdi bu
yapıyı uygulamamıza kayıt etmemiz gerekir ki ne zaman ASP.NET MVC
Makale tipi için bir model bağlama işlemi yapmak istediğinde bizim yazdığı-
mız MakaleBağlayıcı sınıfını kullansın. Bu kayıt işlemini Global.asax dos-
yasında yer alan MvcApplication sınıfı içindeki Applicatipon_Start
metodu içinde yapmamız gerekir.
ModelBinders adlı statik sınıfa ait Binders koleksiyonuna yeni bir eleman
eklemek kaydıyla bu işlemi gerçekleştirebiliriz. Bu koleksiyona ekleyeceği-
miz eleman iki parçadan oluşur: 1) Model sınıfımızın tipi 2) IModelBin-
der’dan kalıt alan bir nesne.
Bizim yazdığımız MakaleBağlayıcı sınıfı DefaultModelBinder sınıfından
kalıt alıyor ve DefaultModelBinder sınıfı da IModelBinder’dan kalıt alı-
yor. Dolayısıyla aşağıdaki şekilde özelleştirilirmiş model bağlayıcımızı kayıt
edebiliriz.
protected void Application_Start() { AreaRegistration.RegisterAllAreas();
//model bağlayıcımızın kayıt edilmesi ModelBinders.Binders.Add(typeof(Makale), new MakaleBağlayıcı());
WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); }
IModelBinder ara-yüzünden direkt olarak kalıt alarak özel model bağlayıcı-
lar geliştirmek de yukarıdaki örneğimizden pek farklı değildir.
ASP.NET MVC Özelleştirilmiş Model Bağlama
10
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
public class MakaleBağlayıcı : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { //yeni (ve tamamen boş bir model nesnesi yaratıyoruz) Makale makale = new Makale(); makale.ID = Convert.ToInt32(bindingContext.ValueProvider.GetValue ("ID").AttemptedValue); makale.Ad = bindingContext.ValueProvider.GetValue ("Ad").AttemptedValue; makale.Metin = bindingContext.ValueProvider.GetValue ("Metin").AttemptedValue; makale.Tarih = Convert.ToDateTime(bindingContext.ValueProvider. GetValue("Tarih").AttemptedValue); //şimdi Etiketler alanını dolduralım //EtiketID adlı HTML alanlardan okuma yapalım string hamVeri = bindingContext.ValueProvider.GetValue ("EtiketID").AttemptedValue; string[] etiketIDleri = hamVeri.Split(','); //veri tabanı bağlamı yarat VTEntities vt = new VTEntities(); //veri tabanından bütün etiketleri oku var bütünEtiketler = vt.Etiketler.ToList(); makale.Etiketler = new HashSet<Etiket>(); //herbir etiketID için foreach (string etiketID in etiketIDleri) { int etkID; if (int.TryParse(etiketID, out etkID)) //etiket ekle makale.Etiketler.Add(bütünEtiketler.Single(o => o.ID == etkID)); } return makale; } }
ASP.NET MVC serimize özelleştirilmiş model bağlayıcı sınıflarını ve bu
yapıların etkin olabilmeleri için nasıl kayıt edildiklerini inceleyerek devam
ettik.
ASP.NET MVC kullanan yazılımcı arkadaşlardan da bilgi, tecrübe ve tav-
siyelerini F5 Dergi ve okuyucularıyla paylaşmalarını rica ediyoruz. Lütfen
yorum, istek ve düşüncelerinizi bize yazın.
http://f5dergi.com/Iletisim veya [email protected]
ASP.NET MVC Özelleştirilmiş Model Bağlama
11
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ASP.NET MVC Özelleştirilmiş Model Bağlama
F5 Dergi yazılımcı
topluma faydalı
içerik üretmeye
aday yazar
arkadaşlar arıyor!
Lütfen bizimle
temasa geçin:
12
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
JavaScript ile Nesne Yönelimli Programlama
İnternet ve web teknolojilerinin günlük yaşamın bir parçası olmaya başladığı
son yıllarda, akıllı telefon ve tablet gibi taşınabilir cihazların artan popülari-
tesine de bağlı olarak JavaScript programlama dili web tabanlı çözümlerin
bir vazgeçilmezi halindedir artık. Apple firmasının iOS işletim sistemiyle
başlattığı “eklentisiz web” dalgası ile Flash ve Silverlight gibi eklenti
(plugin) programlara bağımlı halde çalışan web çözümlerinin yavaş yavaş
gözden düşmesi ve HTML5-CSS3 teknolojilerinin giderek yaygınlaşmasıyla
JavaScript günümüzde eskiye oranla daha sık ve ileri düzeyde kullanılmak-
tadır.
JavaScript artık web sayfalarındaki alert(“lütfen geçerli bir email ad-
resi girin”) tarzı form doğrulama ya da if (n > 0)
document.getElementById(“panel”).style.display = “none”; şeklinde
basit HTML-CSS manipülasyonlarının ötesinde, veri işleyen ve oldukça kar-
maşık işlemler yapabilen bir web programlama dili haline gelmiştir.
Bu nedenlerden dolayıdır ki JavaScript dilini potansiyeline yakışır bir şekil-
de, düzenli ve verimli bir biçimde kullanabilmek gerekir. JavaScript ve nes-
ne yönelimli programlama prensipleriyle nasıl modüler, yeniden kullanılabilir
ve sürdürülebilir JavaScript yazılımları üretebiliriz bunu inceleyeceğimiz ma-
kalemize JavaScript diline has olan temel kavramları gözden geçirerek baş-
layalım.
Tip esnekliği JavaScript klasik programlama dillerinin aksine esnek tiplerden oluşan bir
dildir. Yani Java, C#, C++ vb. dillerde değişkenlerimizi tipleriyle birlikte ta-
nımlamak ve değer atamalarını tiplere uygun olacak şekilde yapmak zorun-
dayken JavaScript dilinde var kelimesiyle değişken tanımlayabilir ve istedi-
ğimiz tipte değerler atayabiliriz. Mesela aşağıdaki ifadeler JavaScript dilinde
hayata sebep olmayacak ifadelerdir.
var değişken = 1; //değişken tanımı ve sayısal değer atama
değişken = "bir"; //aynı değişkene metinsel değer atama
if (değişken == 1 || değişken == "bir") //iki farklı tiple kıyaslama
değişken = new Date("1/1/2012"); //tarih değer atama
JavaScript ile Nesne Yönelimli Programlama
13
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Dinamik bir dil Tip konusunda esnek olmasıyla birlikte mevcut nesnelerin dinamik bir bi-
çimde manipüle edilebilmesi, çalışma esnasında (runtime) bile alan ve me-
tot gibi yapıların nesnelere dinamik olarak eklenebilmesi de JavaScript orta-
mında mümkündür. Bunu bir örnekle şekillendirmek sanırız anlamanın en
iyi yolu olacaktır.
var nesne = new Object(1);//yeni nesne yarat ve değer olarak 1 ata
alert(nesne); //1
nesne.metinsel = "bir"; //nesne’ye dinamik olarak yeni bir
//alan ekle ve bu alana değer ata
alert(nesne.metinsel); //dinamik olarak eklenen alanı oku: "bir"
Örneğimizde de görüldüğü gibi JavaScript ortamında new anahtar kelime-
siyle yaratılan nesnelere dinamik olarak yeni alanlar ekleyebiliriz. Benzer
şekilde mevcut nesnelere dinamik olarak yeni metotlar bile ekleyebiliriz.
//tekmil adlı bir metot ekleyelim
nesne.tekmil = function () { alert(this.metinsel) };
//eklenen fonksiyon içindeki this anahtar kelimesine dikkat
//bu bağlamda this nesnenin kendisini temsil etmekte
nesne.tekmil(); //"bir"
Yukarıdaki kod parçasında nesne değişkenimize tekmil adlı bir fonksiyon
ekliyoruz. Bu yeni fonksiyon içinde nesnenin kendisine this anahtar keli-
mesiyle referans ediyoruz. Bu kavram C# dilindeki this anahtar kelimesi ve
VB.NET dilindeki Me anahtar kelimelerine benzer anlam ifade etmektedir.
Bu örneğimizle yavaş yavaş JavaScript derinliklerine doğru adım atmaya
başlıyoruz. Nesneler üzerine dinamik olarak alan ve metotlar eklemek, ve
içinde bulunulan bağlama göre this anahtar kelimesinin anlamı JavaScript
dilinin önemli kavramlarındandır.
Sınıf yok! Nesne var! JavaScript diyarında sınıf kavramı yoktur! Ancak sınıf kavramının olmaması
nesne kavramının da olmadığı anlamına gelmiyor. JavaScript nesneleri bi-
rer anahtar-değer dizileri olarak düşünülebilirler. Yukarıda değindiğimiz di-
namik alan ekleme veya JSON (JavaScript Object Notation) nesne belirtke-
JavaScript ile Nesne Yönelimli Programlama
14
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
leri yöntemiyle nesneler oldukça basit bir şekilde yaratılabilirler.
//JSON formatında yaratılan bir nesne
var nesne1 = { ad: "F5 Dergi", url: "http://f5dergi.com" };
alert(nesne1.ad); //"F5 Dergi"
//JSON formatında yaratılan bir nesne dizisi
var dizi = [
{ad: "Bing", url: "www.bing.com"},
{ad: "Google", url: "www.google.com"},
{ad: "Yahoo", url: "www.yahoo.com"}
];
alert(dizi[0].ad + ": " + nesneler[0].url); // Bing: www.bing.com
JSON ile yaratılan nesneler veri taşıma işlemlerinde sıklıkla kullanılan bir
yöntemdir. JSON hatta XML’e kıyasla daha az yer kaplayacağından perfor-
mans açısından XML’den daha çok tercih edilmektedir. Ancak JSON kullan-
mak bize gerçek anlamda bir nesne yönelimi sağlamaz. İşlev ve mantığı
yeniden kullanılabilecek modüler yapılar içinde toplayabilmemiz için klasik
dillerdeki sınıf kavramına benzer yapılar oluşturmalıyız. Ancak sınıf kavra-
mının var olmadığı JavaScript dili ile bu nasıl olabilir?
Sınıf yok ama fonksiyon var!
JavaScript dilinin bir başka resmi özelliği fonksiyonları “birinci sınıf” sta-
tüsünde kabul etmesidir. Fonksiyonların birinci sınıf statüsünde olması
kavramını biraz açalım.
Klasik nesne yönelimli programlama dillerinde fonksiyonlar, birinci sınıf de-
ğillerdir. Bu tip dillerde bir fonksiyon sadece ve sadece bir sınıf içinde yer
alabilir ve bir parametre olarak bir sınıftan başka bir sınıfa aktarılamazlar
(delege fonksiyonlar buna bir istisnadır). Yani fonksiyonlar tek başlarına var
olamazlar. Bu yüzden birinci sınıf vatandaş değillerdir. Halbuki JavaScript
dünyasında fonksiyonlar tek başlarına var olabilir ve diğer fonksiyonlar ara-
sında parametre olarak gidip gelebilirler. JavaScript’in bu özelliklerinden do-
layı fonksiyonlar birinci sınıf vatandaş olarak nitelendirilirler.
Şimdi JavaScript fonksiyonların sınıf kavramını nasıl simule edebileceğini
ve bunun bize getirilerini inceleyelim . Personel adlı bir sınıf-fonksiyon ta-
nımlayalım ve bunun klasik sınıf kavramından nasıl farklılıklar taşıdığını ir-
JavaScript ile Nesne Yönelimli Programlama
15
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
deleyelim.
function Personel(ad, soyad, pozisyon) {
//Alanlar
this.Ad = ad;
this.Soyad = soyad;
this.Pozisyon = pozisyon;
//Metot
this.BilgiVer = function () {
alert(this.Ad + " " + this.Soyad + ". " +
this.Pozisyon + " olarak çalışmaktayım.");
};
//Metot
this.PozisyonDeğiştir = function (yeniPozisyon) {
this.Pozisyon = yeniPozisyon;
};
}
var kişi = new Personel("Ali", "Can", "Yazılımcı"); //yeni nesne
kişi.BilgiVer(); //Ali Can. Yazılımcı olarak çalışmaktayım.
kişi.PozisyonDeğiştir("Proje Müdürü");
kişi.BilgiVer(); //Ali Can. Proje Müdürü olarak çalışmaktayım.
Yukarıdaki örnekte Personel adlı bir sınıf-fonksiyon yazdık. Bu fonksiyon
Ad, Soyad ve Pozisyon adlı umumi alanlarla birlikte BilgiVer ve Pozisyon-
Değiştir adlı metotları içinde barındırıyor. Bu tür sınıf-fonksiyonlar yapıcı
(constructor) fonksiyon olarak adlandırılırlar.
JavaScript ortamında new Personel(…, …, …) gibi bir ifadenin tetiklediği
olaylar şöyledir: Yeni ve boş bir nesne yaratılır ve bu nesneye Personel
fonksiyonun içindeki this değeri atanır. (Sanki Personel fonksiyonu re-
turn this ifadesiyle bitiyormuşçasına… Personel fonksiyonu return
this ifadesiyle bitmediği halde new anahtar kelimesini takip eden fonksi-
yonlara uygulanan bir özelliktir bu.)
Bu şekilde yaratılan nesneler üzerinden .Ad, .Soyad, .Pozisyon, .BilgiVer(),
.PozisyonDeğiştir() alan ve metotlarına ulaşabiliriz. Yani sınıf olmayan Ja-
vaScript dilinde ilk sınıfımızı yazdık!
JavaScript ile Nesne Yönelimli Programlama
16
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Burada özenle vurgulamamız gereken bir ayrıntı söz konusudur. Örneği-
mizdeki BilgiVer ve PozisyonDeğiştir metotlarının ana fonksiyon içinde
tanımlanması pratikte kabul edilebilir olsa da, teoride yanlış bir yaklaşımdır.
JavaScript nesnelerinin birer anahtar-değer koleksiyonları olarak düşünüle-
bileceğinden az önce bahsettik. Şimdi yukarıdaki şekliyle yazılmış olan Per-
sonel yapıcı fonksiyonu aracılığıyla yaratılacak nesnelerin bellekte nasıl
tutulacağını görelim.
Buradaki problem yaratılacak her bir nesne için BilgiVer ve PozisyonDeğiş-
tir metotlarının bellekte tekrar ve tekrar yer işgal edecek olmasıdır. Bu du-
rum klasik nesne yönelimli programlama dillerinde (Java, C# vs.) var olma-
yan bir problemdir. İdeal olarak ulaşmamız gereken nokta aşağıdaki şema-
da gösterildiği gibidir.
nesne1 = new Personel("Ad1", "Soyad1", "Pozisyon1")
Ad "Ad1"
Soyad "Soyad1"
Pozisyon "Pozisyon1"
BilgiVer function() {...}
PozisyonDeğiştir function() {...}
nesne2 = new Personel("Ad2", "Soyad2", "Pozisyon2")
Ad "Ad2"
Soyad "Soyad2"
Pozisyon "Pozisyon2"
BilgiVer function() {...}
PozisyonDeğiştir function() {...}
nesne1 = new Personel("Ad1", "Soyad1", "Pozisyon1") BilgiVer function() {...}
Ad "Ad1" PozisyonDeğiştir function() {...}
Soyad "Soyad1"
Pozisyon "Pozisyon1"
BilgiVer ibre
PozisyonDeğiştir ibre
nesne2 = new Personel("Ad2", "Soyad2", "Pozisyon2")
Ad "Ad2"
Soyad "Soyad2"
Pozisyon "Pozisyon2"
BilgiVer ibre
PozisyonDeğiştir ibre
JavaScript ile Nesne Yönelimli Programlama
17
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Klasik nesne yönelimli programlama dillerinde tanımlanan metotlar içinde
tanımlandıkları sınıf üzerinden kaç nesne yaratılırsa yaratılsın bellekte sa-
dece bir defa yer işgal edeceklerdir. Yaratılan nesneler bu bellek adresleri-
ne işaret eden ibreler (pointer) aracılığıyla bu metotlara ulaşabilirler. Ja-
vaScript dilinde bu sonucu elde edebilmemiz JavaScript’in prototype adıyla
bilinen özelliğiyle mümkündür.
Prototype
JavaScript ortamında var olan her yapıcı fonksiyon prototype adında bir
prototip alanına sahiptir. Bu alan bize yapılar arasındaki kalıt alma ilişkilerini
düzenleme imkanı sağlar. JavaScript’e ait Object yapısı bağlamda tanım-
lamış bütün nesneler için kalıt zincirinin ilk halkasını oluşturur ve diğer bü-
tün yapılar direkt veya dolaylı olarak bu Object yapısından kalıt alırlar.
Aşağıdaki kod parçası bize Personel yapısının prototipinin Object tipinde
olduğunu gösterecektir.
alert(Personel.prototype); //[object Object]
Nesneler arasında paylaşılması gereken fonksiyonları direkt olarak yapıcı
fonksiyonumuz içerisinde değil, .prototype özelliğine dinamik olarak ekle-
nen fonksiyonlar olarak tanımlayarak yukarıda sözünü ettiğimiz bellek prob-
lemini ortadan kaldırabiliriz.
//Yapıcı (constructor) fonksiyon
function Personel(ad, soyad, pozisyon) {
this.Ad = ad;
this.Soyad = soyad;
this.Pozisyon = pozisyon;
} //BilgiVer metodunu Personel yapısının prototipine ekliyoruz Personel.prototype.BilgiVer = function () {
alert(this.Ad + " " + this.Soyad + ". " +
this.Pozisyon + " olarak çalışmaktayım.");
}; //PozisyonDeğiştir metodunu Personel yapısının prototipine ekliyoruz Personel.prototype.PozisyonDeğiştir = function (yeniPozisyon) { this.Pozisyon = yeniPozisyon;
};
JavaScript ile Nesne Yönelimli Programlama
18
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
//Kalıt ağacının kökündeki Object yapısı tarafından tanımla-nan .toString metodunu yeniden yazıyoruz Personel.prototype.toString = function () { return "Personel"; };
NOT: Personel yapımız içinde toString metodunu da tanımlıyoruz. Bu az
sonra bu yapıdan kalıt alacak yapıları teşhis etmemizi kolaylaştıracak.
Şimdi Personel yapısı üzerinden yaratacağımız nesnelere ait BilgiVer ve
PozisyonDeğiştir metotları bellekte yalnızca bir kere yer işgal edecekler-
dir.
Peki şimdi Personel yapımızdan kalıt alacak başka bir yapı düşünelim: Yö-
netici. Her bir yönetici aynı zamanda bir personel olacağından Yönetici ya-
pısını Personel yapısının bir alt sınıfı olarak yazalım.
function Yönetici(ad, soyad, pozisyon) {
//üst sınıfın yapıcı (constructor) metodunu çağır
Personel.call(this, ad, soyad, pozisyon);
this.Elemanlar = []; //boş elemanlar dizisi
}
//Personel yapısından kalıt al
Yönetici.prototype = new Personel();
//yeni ve sadece yöneticiye ait metot
Yönetici.prototype.ElemanEkle = function (personel) {
this.Elemanlar.push(personel);
};
Yönetici ve Personel yapıları arasındaki alt sınıf-üst sınıf ilişkisini Yönetici
yapısının prototype alanına yeni bir Personel nesnesi atayarak gerçekleş-
tiriyoruz ki Personel yapısına ait olan Ad, Soyad, Pozisyon alanları ve Bilgi-
Ver, PozisyonDeğiştir metotları Yönetici tipinde yaratılacak nesneler üzerin-
den de ulaşılabilsin.
Örnek kullanım şekli olarak aşağıdaki kodu inceleyelim.
var yönetici = new Yönetici("Veli", "Ak", "Genel Müdür");
yönetici.BilgiVer(); //alt sınıf metodu
JavaScript ile Nesne Yönelimli Programlama
19
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
//yeni bir eleman yarat ve yöneticiye ekle
var eleman = new Personel("Ali", "Can", "Yazılımcı");
yönetici.ElemanEkle(eleman); //üst sınıf metodu
alert(Yönetici.prototype); //Yöneticinin prototipi Personel
Toparlamak gerekirse JavaScript’in prototype özelliği aracılığıyla gerçekleş-
tirilebilecek iki konuyu inceledik:
Bellekte gereksiz yer işgal etmeyen sınıf-benzeri yapıların nasıl tanım-
lanabileceği
Kalıt alma
Ancak dikkat ederseniz prototip tekniği tanımladığımız yapıların modülerliği-
nin bozulmasına sebep oldu. Daha önce tek ve bağımsız bir fonksiyon için-
de sınıf-benzeri yapılarımızı tanımlayabilirken, şimdi elimizde birbirine ba-
ğımlı ama ayrık yapılar var!
Peki yapılarımızı hem modüler hem de
bellek kullanımı açısından etkin bir bi-
çimde yazmak mümkün mü? Haziran-
Temmuz 2012 sayımızda da inceledi-
ğimiz kendini çağıran JavaScript ka-
panımları bunu mümkün kılmaktadır.
Yapıcı metot ve Personel.prototype
Modüler Teknik Prototip Tekniği
function Personel(ad, soyad, pozisyon)
{
this.Ad = ad;
this.Soyad = soyad;
this.Pozisyon = pozisyon;
this.BilgiVer = function () {...};
...
}
function Personel(ad, soyad, pozisyon)
{
this.Ad = ad;
this.Soyad = soyad;
this.Pozisyon = pozisyon;
}
Personel.prototype.BilgiVer = function
() {...};
...
Modüler ancak bellek açısından
etkin değil
Bellek açısından etkin ancak mo-
düler değil
JavaScript ile Nesne Yönelimli Programlama
F5 Makale: JavaScript Kapa-
nımları ve Kendini Çağıran
Fonksiyonlar (http://
f5dergi.com/Makale/a/14)
20
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
alanına eklediğimiz metotları kendini çağıran bir fonksiyon içine kapatarak
kaybettiğimiz modülerliği yeniden kazanabiliriz.
var Personel = (function() { //kapanım fonksiyonu aç
//yapıcı (constructor) metot
function Personel(ad, soyad, pozisyon) {
this.Ad = ad;
this.Soyad = soyad;
this.Pozisyon = pozisyon;
}
//BilgiVer metodunu Personel yapısının prototipine ekliyoruz
Personel.prototype.BilgiVer = function () {
alert(this.Ad + " " + this.Soyad + ". " +
this.Pozisyon + " olarak çalışmaktayım.");
};
//PozisyonDeğiştir metodunu Personel yapısının prototipine ekliyoruz
Personel.prototype.PozisyonDeğiştir = function (yeniPozisyon) {
this.Pozisyon = yeniPozisyon;
};
//Kalıt ağacının kökündeki Object yapısı tarafından
// tanımlanan .toString metodunu yeniden yazıyoruz
Personel.prototype.toString = function () {
return "Personel";
};
return Personel; //yapıcı metodu döndür
} //kapanım fonksiyonunu kapat
)(); //kapanım fonksiyonu: kendini çağır
Personel yapımızı bu şekilde yazmak kullanıcı kod tarafında hiçbir değişikli-
ğe sebep olmayacaktır:
var kişi = new Personel("Ali", "Can", "Yazılımcı");
Personel adlı yapımızı ve bu yapının prototype alanına eklediğimiz metot-
ları kendini çağıran adsız bir kapanım fonksiyonu ile çevrelemek, ve bu
fonksiyonun içinden yapıcı (constructor) fonksiyonu döndürmek (return
JavaScript ile Nesne Yönelimli Programlama
21
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Personel), ve son olarak da bu dönen referansı Personel adlı bir değişke-
ne atamak suretiyle sınıf-benzeri yapımızı modüler bir şekilde tanımlamış
oluyoruz.
Sınıf kavramının olmadığı JavaScript dili ile nesne yönelimli programlama
konusunun temellerini inceledik. Son yıllarda JavaScript’in artan ve karma-
şıklaşan kullanımına paralel olarak JavaScript kod üretimini kolaylaştırmak
amacıyla birtakım aracı diller türemiştir. CoffeScript ve Microsoft tarafın-
dan şu sıralar ön-izleme (preview) versiyonundaki TypeScript bunlara ör-
nek dillerdir. Bu diller yeni kodlama imlaları tanımlasalar da derlenmiş hal-
leri yine bildiğimiz JavaScript’in ta kendisidir. Bu durum JavaScript’in artan
önemine net bir işarettir. Bu nedenle JavaScript'i iyi anlamak, potansiyelinin
farkına varmak ve etkin kullanabilmek için kendimizi geliştirmek yazılımcılar
için kaçınılmaz hale gelmektedir.
Yorum, istek ve düşüncelerinizi lütfen bizimle paylaşın.
http://f5dergi.com/Iletisim veya [email protected]
JavaScript ile Nesne Yönelimli Programlama
F5 Dergi’de okumak
istediğiniz konuları
bize yazın.
22
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Duyarlı Web Tasarımları ve CSS Medya Sorguları
Tablet ve telefon gibi taşınabilir cihazların artan kullanımı web tasarımı sek-
töründe de bir takım değişiklikleri ve yenilikleri beraberinde getirmektedir.
Bunların başında web sitelerinin ve web tabanlı uygulamaların taşınabilir
cihazlarda da rahat kullanılabileceği dinamik ve modern ara yüzler tasarlan-
ması gerekliliğidir.
Bu sayımızda web sayfalarının kullanılan cihaz ve tarayıcıların özellik ve
kabiliyetlerine uygun şekilde sunulmalarını amaçlayan duyarlı web tasarı-
mı (responsive web design) konusunu ve duyarlı web tasarımların bir
uzantısı olan CSS medya sorguları (media queries) olarak bilinen teknik-
leri inceleyeceğiz.
Duyarlı! Nasıl yani?
Duyarlı web tasarımı kavramı HTML içerikleri kesin ve kati ölçütler kullan-
madan, sabit ve mutlak (absolute) pozisyonlamalar yapmadan, esnek ve
tarayıcının ebatlarındaki değişikliklere dinamik bir biçimde uyum sağlayabi-
lecek tasarım yöntemlerinin tümüne verilen addır.
Duyarlı web tasarım tekniklerinin ilk ve en basit örneklerinden biri sanırız
HTML elemanlarının ebat ölçülerinde piksel (px) yerine yüzde (%) kullanıl-
ması tekniğidir. Mesela, aşağıdaki HTML tablo, genişliği 900px’den az olan
tarayıcı pencerelerinde yatay kaydırma çubuğunun ortaya çıkmasına ve bu-
nun bir sonucu olarak kullanım zorluğuna sebep olacaktır.
<table style="width: 900px;"> <tr> <td>...</td> </tr> </table>
Halbuki width: 100% şeklinde stillenen bir tablo tarayıcının enine duyarlı
bir biçimde uyum sağlayacak ve yatay kaydırma çubuğuna genellikle sebep
olmayacaktır. Bu teknik tam bir çözüm olmamakla beraber yüzde değerler
kullanılarak oluşturulan tasarımlar masaüstü, tablet ve akıllı telefon gibi
farklı büyüklüklerdeki cihazlarda görsel açıdan daha kullanışlı sonuçlar ve-
recektir ve bu tip cihazlarda ortaya çıkabilecek kullanım zorluğunu azalta-
Duyarlı Web Tasarımları ve CSS Medya Sorguları
23
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
caktır.
Duyarlı tasarım teknikleri için verebi-
leceğimiz ikinci örnek CSS float tek-
niğidir. Bu CSS tekniğini kullanarak
HTML elemanlarını tarayıcı pencere-
sinin genişliği müsaade ettiği müd-
detçe yan yana pozisyonlandırabili-
riz. Pencerenin genişliği azaldıkça HTML elemanları yatay kaydırma çubu-
ğuna yol açmaksızın dinamik olarak sayfa üzerinde yer değiştireceklerdir.
Bunu aşağıdaki örnek HTML sayfa ile simule edebiliriz.
<!DOCTYPE html> <head> <title>F5 Dergi CSS Float Örneği</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> body {font-size: 35px;} div {float: left;} #logo {float: left; width: 200px; min-height: 100px;} #menü {float: left; width: 350px; min-height: 100px;} #içerik1 {float: left; width: 350px; min-height: 200px; padding-right: 20px; padding-bottom: 30px;} #içerik2 {float: left; width: 350px; min-height: 200px; padding-right: 20px; padding-bottom: 30px;} #içerik3 {float: left; width: 200px; min-height: 200px;} </style> </head> <body> <div> <div id="logo">logo</div> <div id="menü">menü menü menü menü</div> </div> <div style="clear: both"></div> <div> <div id="içerik1"> içerik1... içerik1... içerik1... içerik1...
Duyarlı Web Tasarımları ve CSS Medya Sorguları
Örnek HTML Sayfa:
http://f5dergi.com/indir/
kod/2012.11/CSS/float.html
(Üyelik gerektirmektedir)
24
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
içerik1... içerik1... içerik1... içerik1... içerik1... içerik1... içerik1... içerik1... </div> <div id="içerik2"> içerik2... içerik2... içerik2... içerik2... içerik2... içerik2... içerik2... içerik2... </div> </div> <div id="içerik3"> içerik3... içerik3... içerik3... içerik3... </div> </body>
Yukarıdaki HTML tarayıcı penceresinin 900px (+ padding) veya daha büyük
olduğu durumlarda şöyle bir çıktı verecektir:
Aynı HTML pencere 700px
(+ padding değerler) ile
900px (+ padding değerler)
arası bir genişliğe sahipse:
Pencere 550px (+ padding
değerler) ile 700px (+ pad-
ding değerler) arası bir ge-
nişliğe sahipse:
Ve pencere 550px’den
daha az bir genişliğe
sahipse:
Duyarlı Web Tasarımları ve CSS Medya Sorguları
25
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Yukarıdaki ekran çıktıları pek tabii ki CSS float tekniğine basit örnekler ola-
rak hazırlanmıştır. Buradaki maksat duyarlı web tasarımı konusunun sa-
dece modern tarayıcılar tarafından desteklenen gelişmiş medya sorgulama
tekniklerinden ibaret olmadığına işaret etmektir.
CSS medya sorguları nedir?
Medya sorguları farklı CSS sınıflarının bir takım şartlara bağlı olarak HTML
üzerinde etkin kılınmasına olanak sağlayan sorgulama yöntemleridir. Aslın-
da bunun en eski ve bilinen örneği ekran (screen) ve baskı (print) medya
tiplerini hedef alan medya sorgularıdır.
<link rel="stylesheet" href="/css/ekran.css"
type="text/css" media="screen" />
<link rel="stylesheet" href="/css/yazici.css"
type="text/css" media="print" />
Buradaki mantık ekran ve yazıcı cihazları için farklı CSS içerikleri yaratmak
ve bunu medya tipine (printer veya ekran) bağlı olarak dinamik bir şekilde
HTML içerik üzerinde etkin kılmaktır. Böylece kullanıcılar HTML sayfaların
dökümünü almak istediklerinde ekran için optimize edilmiş ve muhtemelen
baskıda pek hoş olmayacak bir tasarım yerine farklı bir CSS dosya ile baskı
için optimize edilmiş görselliklerin etkin olduğu bir sonuç elde edeceklerdir.
Modern tarayıcılar tarafından desteklenmeye başlayan CSS3 medya sor-
guları da benzer mantık ile, HTML içeriklere masaüstü bilgisayar ekranında
görüntülendiklerinde başka görsellik, cep telefonu ekranında görüntülendik-
lerinde başka görsellik uygulamamızı mümkün kılan bir tekniktir. Mesela
aşağıdaki blok ile yüklenen CSS dosyası sadece tarayıcı penceresinin ge-
nişliği 996px veya daha geniş olduğu durumlarda etkin olacaktır.
<link rel="stylesheet" href="monitor.css" type="text/css"
media="screen and (min-width: 996px)" />
Medya sorgularının nasıl yazılabileceği ve medya sorguları kullanarak nasıl
HTML ve CSS manipülasyonu yapılabileceğini irdelemeye başlamadan ön-
ce, gelin CSS içeriklerinin HTML sayfaları üzerinde hangi yöntemlerle etkin
kılınabildiğini hatırlayalım.
Duyarlı Web Tasarımları ve CSS Medya Sorguları
26
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Link bloğu
HTML dosyasının <head> bloğu içine yerleştirilen <link> bloğu ile sayfa dı-
şından CSS dosyası yükleme:
<link rel="stylesheet" href="ekran.css" type="text/css" media="screen"/>
<link rel="stylesheet" href="yazici.css" type="text/css" media="print"/>
HTML sayfa içinde
HTML dosyasının <head> bloğu içine yerleştirilen <style> bloğu içine direkt
olarak yerleştirilen CSS içeriği:
<style type="text/css" media="screen"> p { color: #0000FF; } </style> <style type="text/css" media="print"> p { color: #000000; } </style>
@import bildirimi ile
HTML dosyasının <head> bloğu içine yerleştirilen <style> bloğu içinden ve-
ya dışarıdan yüklenen CSS dosyaları içinden @import bildirimiyle dışarıdan
CSS dosya yükleme:
<style type="text/css"> @import "ekran.css" screen; @import "yazici.css" print; </style> Şimdi medya sorgularında sıklıkla kullanılan kriterleri ve bilinmesi gereken
teknikleri inceleyelim.
CSS3 medya sorguları Ekran (screen) ve baskı (print) medya tiplerini hedefleyen medya sorguları-
nın daha gelişmiş bir hali olan CSS3 medya sorgularında and (ve), not
(değil), all (bütün) ve only (sadece) gibi mantık operatörlerinin yanı sıra ve-
ya anlamına gelecek virgül (,) operatörü de kullanılabilir.
CSS3 medya sorgularında kullanılan kriterleri yakından incelemeye başla-
madan önce mantık operatörlerinin nasıl kullanılabileceğini bir kaç örnekle
somutlaştıralım.
Duyarlı Web Tasarımları ve CSS Medya Sorguları
27
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Genişliği en az 478px olan ekran tarayıcı pencereleri için etkin olacak CSS içerik içinde alt küme tanımlayarak: @media only screen and (min-width : 478px) { .görsel1 {/*...*/} #solSütun {/*...*/} } Yazıcı çıktısı veya ekranda genişliği en fazla 767px olan tarayıcı pencerele-
ri için <link> bloğu ile:
<link rel="stylesheet"
media="only print, screen and (max-width: 767px), " />
Ekranda genişliği en fazla 900px olan tarayıcı pencerelerinde etkin olacak
@import yöntemiyle:
<style> @import "cep.css" only screen and (max-width:900px); </style>
only operatörü CSS3 medya sorguları ile referans edilen CSS içeriklerin
CSS3 medya sorgularının desteklenmediği tarayıcılar tarafından görmez-
den gelinmesini sağlamak için kullanılır. Böylece bu özelliklerin desteklen-
mediği tarayıcılarda beklenmedik sonuçlar önlenmiş olur. Bu teoride doğru
olan bir ifadedir ancak Internet Explorer’ın CSS3 medya sorgularını destek-
lemeyen versiyonları only ibaresi olmasa da CSS3 medya sorguları kullanı-
lan CSS referanslarını görmezden gelmektedir.
all operatörü de bütün medya tipleri anlamına gelmektedir. Ancak screen
veya print gibi spesifik bir medya tipi deklare edilmemişse bu all olarak al-
gılanacağı için all operatörü pek kullanılmayan bir operatördür. Referans
bilgi olarak bütün medya tiplerini aşağıda listeleyelim.
aural (işitme cihazı)
braille (görme özürlüler için kabartma yazı)
embossed (kabartma)
handheld (avuç içi / elde taşınabilen cihaz)
print (baskı)
projection (yansıtma / tepegöz)
screen (ekran)
tty (tele-daktilo)
tv (televizyon)
CSS3 medya sorgularında kullanılabilen mantık operatörlerini ve medya
Duyarlı Web Tasarımları ve CSS Medya Sorguları
28
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
tiplerini inceledikten sonra şimdi CSS3 medya sorgularında kullanılan kriter-
leri incelemeye başlayalım.
width (genişlik) kriteri
Medya sorgularında en çok kullanılan bu kriter HTML sayfasının görüntü-
lendiği tarayıcı penceresinin genişliğini sorgulamamıza olanak sağlayan bir
kriter olan width ayrıca min– ve max– öneklerini de desteklemektedir. Ge-
nellikle min– ve max– önekleriyle kullanılan bu sorgulama kriterini bir örnek-
le inceleyelim.
Farz edelim ki HTML içeriğimizi cep telefonlarında farklı bir görsellik ile ser-
vis etmek istiyoruz. Bunun için cep.css ve monitör.css adlı iki farklı dosya-
mız olduğunu kabul edelim. Ve HTML sayfamızdan bu iki farklı CSS dosya-
sına aşağıdaki şekilde bağlantı yapalım.
<!DOCTYPE html> <head> <!--Genişlik 768px veya daha büyükse monitör.css dosyasını yükle--> <link rel="stylesheet" href="monitör.css" type="text/css"
media="screen and (min-width: 768px)" />
<!-- Genişlik 767px veya daha az ise cep.css dosyasını yükle -->
<link rel="stylesheet" href="cep.css" type="text/css"
media="screen and (max-width: 767px)" />
</head>
<body> <h1>CSS Medya Sorguları</h1> <p>F5 Dergi CSS Sorguları makalesi test sayfası...</p> </body> </html>
Yukarıda yorum olarak verilmiş notlarda da vurgulandığı üzere tarayıcı ge-
nişliğine bağlı olarak uygun CSS dosyası HTML içerik üzerinde etkin ola-
caktır. Örneğimizi somutlaştırmak adına bu farklı CSS dosyalarını farklı
punto ve renklerle şu şekilde oluşturalım:
Monitör.css
body { font-size: 10px; font-family: Verdana; color: Blue; }
Cep.css
body { font-size: 26px; font-family: Verdana; color: Red; }
Duyarlı Web Tasarımları ve CSS Medya Sorguları
29
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Hazırladığımız http://lab.f5dergi.com/201211/cms/test.html sayfasında da
görebileceğiniz gibi HTML içerik tarayıcı genişliğine göre farklı bir görsellik
kazanacaktır.
Masaüstü/Dizüstü Monitör veya tablet
Cep telefonu (iPhone, Android, Windows Phone vb.)
İlkel bir örnek de olsa medya sorgularını kullanarak değişik koşullar için na-
sıl değişik CSS dosyalarının etkin kılınabildiğini görmüş olduk. Şimdi örne-
ğimizi daha gerçekçi bir duruma getirerek konuyu irdelemeye devam ede-
lim. HTML içeriğimizi aşağıdaki gibi geliştirelim.
NOT: Ana bileşenler haricindeki örnek içerik metinler özellikle gizlenmekte-
dir. Okuyucularımız örnek sayfadan tam içeriğe ulaşabilirler.
<body> <div id="üst"> <!-- logo --> <ul class="nav"> <!-- navigasyon --> </ul> </div> <div id="ana"> <div id="kol1"> <!-- 1. içerik sütunu --> </div>
Duyarlı Web Tasarımları ve CSS Medya Sorguları
Örnek HTML Sayfa
http://f5dergi.com/indir/
kod/2012.11/CSS/cssms.html
(Üyelik gerektirmektedir)
30
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
<div id="kol2"> <div class="anaMetin"> <h1>Türkçe Yazılım ve Programcılık Dergisi</h1> <div class="anaResim"> <!-- Ana Resim --> </div> <!-- 2. içerik sütunu --> </div> </div> <div id="kol3"> <div style="clear: both"> <!-- 3. içerik sütunu --> </div> </div> </div> </body>
Logo ve navigasyon panelleri içeren üst bölümünden sonra üç sütundan oluşuyor HTML içeriğimiz. Monitör.css body { font-family: Verdana; color: #212020;} #üst {background: #006B94}
.nav {list-style: none; margin: 0; padding: 0; float: left; width: 100%; background: #006B94; margin-bottom: 20px;}
.nav li {float: left; font-size: 14px; font-weight: bold; padding: 5px; padding-right: 50px;} .nav li a {color: white; min-width: 150px; height: 35px; text-decoration: none; } h1 {font-size: 2em; margin-top: 0;} h2 {font-size: 1.5em; margin-top: 0;} h3 {font-size: 1.3em; margin-top: 0;}
#kol1 {float: left; width: 18%; margin-right: 15px; padding-right: 10px; border-right: 1px solid #ddd; font-size: 0.8em}
#kol2 {float: left; width: 53%; font-size: 1em; padding-right: 15px;}
#kol3 {float: left; width: 21%; border-left: 1px solid #ddd; font-size: 0.72em; padding-left: 15px}
.anaResim {float: right; margin-left: 10px;}
Duyarlı Web Tasarımları ve CSS Medya Sorguları
31
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Bu CSS bize şöyle bir sonuç verecektir:
Burada dikkat çekmek istediğimiz nokta üç sütunun float: left ile yatay
bir şekilde sıralanmasıdır. Ayrıca ikinci sütun içinde yer alan resim float:
right ile metnin sağ tarafına doğru gömülmüştür. Şimdi cep.css dosyamı-
zı geliştirelim.
Cep.css
body {font-family: Verdana; color: #212020;} #üst {background: #006B94} .nav {list-style: none; margin: 0; padding: 0; float: left; width: 100%; background: #006B94; margin-bottom: 20px;} .nav li {float: left; font-size: 14px; font-weight: bold; padding: 5px; padding-right: 50px; min-width: 85px; } .nav li a {color: white; min-width: 150px; height: 35px; text-decoration: none;} h1 {font-size: 1.8em; margin-top: 0;} h2 {font-size: 1.5em; margin-top: 0;} h3 {font-size: 1.3em; margin-top: 0;}
#kol1 {display: none;} #kol2 {width: 100%; font-size: 1em; } #kol3 {width: 100%; font-size: 0.72em; margin-top: 15px;}
.anaResim img {max-width: 150px; margin-bottom: 15px;}
Duyarlı Web Tasarımları ve CSS Medya Sorguları
32
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Bu CSS de bize şöyle bir sonuç verecektir:
Genişliği 767px veya daha az olan tarayıcılar üzerinde etkin olacak cep.css
dosyası ile monitör.css dosyası arasındaki farkları inceleyelim şimdi:
Cep.css kol1 panelini display: none ifadesiyle saklıyor. Mobil cihazlarda
tarayıcı pencerelerinde yer limitli olduğundan sol sütunu saklamayı tercih
ettik ki sayfa üzerindeki en önemli içerik grubu olarak değerlendirdiğimiz
kol2 panelini okuyucuya doğrudan sunabilelim. Ayrıca kol2 ve kol3 panelle-
rinden float: left ifadelerini kaldırdık ve width: 100% ile genişliklerin ta-
rayıcı genişliğine eşitledik. Bu da bu sütunların yatay değil dikey olarak sıra-
lanması anlamına geliyor. Ve son olarak anaResim sınıfına max-width:
150px ifadesini ekleyerek resmin biraz daha küçük olarak sunulmasını sağ-
ladık.
Medya sorgularının web tasarım sektöründe profesyonel tasarımcı ve prog-
ramcılar tarafından nasıl kullanıldığını Microsoft’un Visual Studio 2012 mik-
ro-sitesini (http://www.microsoft.com/visualstudio) yakından inceleyerek da-
ha iyi özümseyebiliriz. Gelin bu güzel sitenin akıllı telefon ve diğer cihazlar-
da (tablet, masaüstü, dizüstü) nasıl farklı görsellik ile sunulduğunu görelim.
Duyarlı Web Tasarımları ve CSS Medya Sorguları
33
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Masaüstü/Dizüstü/Tablet Ekranı
Buradaki yalın tasarımda bir arka plan grafiği üzerine yerleştirilmiş bir
anons paneli ve alt kısımda yer alan ana navigasyon menüsü göze çarp-
maktadır.
Akıllı telefon ekranı
Duyarlı Web Tasarımları ve CSS Medya Sorguları
34
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Aynı HTML sayfasının tarayıcı genişliği belirli bir değerin altındaysa nasıl
farklı bir tasarım ile sunulduğunu kolaylıkla görebiliyoruz. Anons paneli arka
plan resminin altında yer alan gri bir panele dönüştü ve alt kısımda yer alan
ana navigasyon sayfanın en üstüne çıktı ve menü gizlendi. Bütün bunlar
CSS medya sorgularıyla mümkün olmaktadır.
Microsoft Visual Studio web sitesinde kullanılan bazı CSS3 medya sorguları
@media only screen and (min-width : 478px) {/**/} @media only screen and (min-width : 768px) {/**/} @media only screen and (min-width : 996px) {/**/} @media only screen and (min-width : 1200px) {/**/}
En sık kullanılan min-width ve max-width ile tarayıcının pencere genişliğine
bağlı olarak değişik görsellik uygulamayı örneklerle inceledik. Diğer medya
sorguları kriterlerine geçmeden önce şu iki noktayı vurgulamamız gerekir:
1) Medya sorgularını desteklemeyen tarayıcılar
CSS3 medya sorguları teknikleri ile tasarlanmış web siteleri eski ve bu özel-
liği desteklemeyen tarayıcılar tarafından ziyarete edilirse sonuç ne olacak?
Eğer HTML sayfalar tarafından kullanılan CSS içeriklerine yapılan referans-
lar medya sorguları ile yazılmışsa, medya sorgularını desteklemeyen tarayı-
cılar bu CSS içeriklerini yükleyemeyecek ve sonuç oldukça vahim olacaktır.
O yüzden bütün şartlarda ortak olarak kullanılacak olan CSS sınıflarını bir
gruba toplayıp bu grubu medya sorguları kullanılmayan bir referansla yükle-
memiz gerekir.
Şimdi bunu örneğimize uygulayalım:
ÖNCEKİ HAL <!--Genişlik 768px veya daha büyükse monitör.css dosyasını yükle--> <link rel="stylesheet" href="monitör.css" type="text/css"
media="screen and (min-width: 768px)" />
<!-- Genişlik 767px veya daha az ise cep.css dosyasını yükle -->
<link rel="stylesheet" href="cep.css" type="text/css"
media="screen and (max-width: 767px)" />
Duyarlı Web Tasarımları ve CSS Medya Sorguları
35
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
SONRAKİ HAL
<!--Her hâlükârda yüklensin monitör.css-->
<link rel="stylesheet" href="monitör.css" type="text/css"
media="screen" />
<!-- Genişlik 767px veya daha az ise cep.css dosyasını yükle -->
<link rel="stylesheet" href="cep.css" type="text/css"
media="screen and (max-width: 767px)" />
Burada ilk <link> referansındaki (min-width: 768px) ifadesini kaldırıyoruz
ki monitör.css her hâlükârda yüklensin. (max-width: 767px) şartı gerçek-
leştiğinde yüklenecek olan cep.css sınıfı tarafından tanımlanan görsellik bu
dosyanın monitör.css dosyasından sonra yükleneceği için cep.css dosya-
sında tanımlanan sınıflar etkin olacaktır. Bu yöntem sayesinde ayrıca iki
dosya arasında ortak olan stiller cep.css sınıfından silinebilir.
2) Medya sorgularının yüklenme sırası
Medya sorgularını kullanırken dikkat etmemiz gereken bir diğer nokta da
CSS içeriklerin yüklenme sırasıdır. Bu sadece medya sorgularına değil
CSS’e has bir özelliktir. Bu konuyu bir örnekle görebilmek için aşağıdaki
HTML ifadelerini ele alalım.
<link rel="stylesheet" href="500.css" type="text/css" media="screen and (min-width: 500px)" /> <link rel="stylesheet" href="800.css" type="text/css" media="screen and (min-width: 800px)" />
Burada pencere genişliği 900px olan bir tarayıcı hem 500.css hem de
800.css dosyalarını yükleyecektir. Çünkü genişliği 900px olan bir tarayıcı
min-width: 500px ve min-width: 800px şartlarından her ikisini de sağlaya-
caktır. Bu da şu anlama gelecektir:
Eğer aynı CSS sınıfı her iki dosyada da mevcut ise, en son yüklenen (yani
800.css) CSS dosyasındaki tanım etkin olacaktır (eğer 500.css içinde ta-
nımlanan ifadeler !important ibaresini içermiyorsa). Bu yüzden medya
sorgularıyla birden fazla CSS dosyasını değişik şartlara bağlı olarak yükle-
mek istiyorsak bu özelliği göz önünde bulundurmamız gerekir.
Şimdi diğer sık kullanılan kriterleri tanıyalım.
Duyarlı Web Tasarımları ve CSS Medya Sorguları
36
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
height (yükseklik) kriteri
Bu kriter HTML sayfasının görüntülendiği tarayıcı penceresinin yüksekliğini
sorgulamamıza olanak sağlayan bir kriterdir, min– ve max– öneklerini de
desteklemektedir.
device-width (cihaz-genişliği) kriteri
Width kriteri ile tarayıcının genişliğini sorgulayabilirken, device-width bize
cihazın genişliğini sorgulama imkanı verir. Device-width min– ve max–
öneklerini desteklemektedir. Çoğu tablet ve akıllı telefonlarda width ve devi-
ce-width pratikte aynı işi görmektedirler, çünkü bu tip cihazlarda kullanıcı
tarayıcının ebatlarını değiştiremez. Ancak dizüstü ve masaüstü bilgisayar-
larda width ve device-width kriterleri değişik sonuçlar doğuracaktır. Width
kriteri kullanarak yazılan medya sorguları (min-width ve/veya max-width gi-
bi) dizüstü ve masaüstü bilgisayarlarda tarayıcı ebatlarında yapılan manuel
değişikliklere tepki gösterirken, device-width ile yazılan medya sorguları
(min-device-width ve/veya max-device-width gibi) tarayıcı ebat değişiklikleri
görmezden gelinecektir.
device-height (cihaz-yüksekliği) kriteri
Device-width kriterine benzer şekilde device-height bize cihaz yüksekliğini
sorgulama imkanı verir. Device-height min– ve max– öneklerini destekle-
mektedir.
orientation (yönlendirme) kriteri
Landscape ya da portrait değerlerinden birini alabilen bu kriter ile tarayıcı
penceresinin yatay mı (landscape) yoksa dikey mi (portrait) olduğunu sor-
gulayabiliriz. Orientation kriteri cep telefonu ve tablet gibi hem yatay hem
dikey tutulabilen cihazlarda dikey (portrait) durumda daha büyük bir punto
(font) ile kullanıcıya okuma kolaylığı sağlanabilir.
@media screen and (orientation: portrait)
aspect-ratio (görünüm-orantısı) kriteri
Bu kriter ile tarayıcının yatay ve dikey ebatlarının birbirine olan orantısını
sorgulayabiliriz. Bu kriterin alabileceği değerler X/Y şeklindedir (X tarayıcı
penceresinin eni, Y boyu olacak şekilde). Aspect-ratio kriteri min– ve max–
öneklerini desteklemektedir.
En/Boy orantısı en az 4/3 olan tarayıcılara uygulanacak örnek sorgu:
@media screen and (min-aspect-ratio: 4/3)
Duyarlı Web Tasarımları ve CSS Medya Sorguları
37
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
device-aspect-ratio (cihaz-görünüm-orantısı) kriteri
Aspect-ratio kriterine benzer şekilde device-aspect-ratio bize tarayıcının
değil cihaz ekranının yatay ve dikey ebatlarının birbirine olan orantısını sor-
gulama imkanı verir. Bu kriter min– ve max– öneklerini desteklemektedir.
resolution (çözünürlük) kriteri
Çıktı alınan cihazın (ekran, printer, tv vs. ) çözünürlüğünü sorgulamamızı
sağlayan bu kriter dpi (dots per inch) veya dpcm (dots per centimeter) for-
matında sayısal değerler alabilir. Bu kriter min– ve max– öneklerini destek-
lemektedir.
Çözünürlüğü en az 300dpi olan baskı araçlarına uygulanacak örnek sorgu:
@media print and (min-resolution: 300dpi)
Duyarlı web tasarımı konusunu ve modern tarayıcılar tarafından destekle-
nen CSS3 medya sorguları tekniklerini tanıttığımız makalemizi faydalı ve
ilham kaynağı olabilecek linklerle bitirelim.
Örnek duyarlı web tasarımları
http://sixrevisions.com/design-showcase-inspiration/responsive-webdesign-
examples/
http://thenextweb.com/dd/2012/02/01/10-beautiful-examples-of-responsive-
web-design/
http://www.ourtuts.com/responsive-website-design-examples/
Yorum, istek ve düşüncelerinizi lütfen bizimle paylaşın.
http://f5dergi.com/Iletisim veya [email protected]
NOT: Bu makalede incelediğimiz konular tamamen tarayıcı tarafında etkin olan
yöntemlerden ibarettir. Taşınabilir cihazların (özellikle akıllı telefonların) sunucu
tarafında tespiti ve tamamen farklı HTML içerik gönderme ilkesine dayalı tek-
nikler de mevcuttur. Özellikle ASP.NET MVC temelli projelerde çok az ekstra
işle oldukça yaratıcı sonuçların alınabildiği çözümler mevcuttur. Okuyucuları-
mızdan gelecek talepler doğrultusunda bu konuları da işleyebiliriz.
Duyarlı Web Tasarımları ve CSS Medya Sorguları
38
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Yazılım ve
programcılık
dergisi.
BIRAKIN BİLGİ SİZE GELSİN
Ücretsiz abonelik: http://f5dergi.com
39
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Entity Framework Microsoft’un veri tabanı bileşenlerini (tablo vb.) eşleştirme (Object-
Relational Mapping / ORM olarak bilinen konsept) yoluyla nesnelere dönüş-
türen yazılım geliştirme aracı Entity Framework (EF), sektörde sıklıkla kulla-
nılan ve artık açık kaynak (open source) olarak yazılımcı toplumun da katılı-
mıyla sürekli gelişmekte olan bir üründür.
EF’i detaylı bir şekilde tanımaya başlamadan önce biraz bu işlerin evveliya-
tına gidelim. EF ve benzer yazılım araçlarından önce veri tabanında kayıtlı
bilgilerin uygulamalarımızda değişkenler ve nesneler içine okunması, ve bu
değerlerin sonra yine veri tabanına kaydedilmesi işlemleri yazılımcılar tara-
fından bizzat gerçekleştirilen işlemlerdi. Daha somut ifade etmek gerekirse
veriler IDataReader, IDataCommand, DataTable, DataRow vs. gibi yapı-
ların yardımıyla veri tabanı ortamından okunuyor ve bir takım manipülas-
yonlardan sonra veri tabanına yeniden kaydediliyordu.
Bu yöntem tabii ki iş görüyordu ama birtakım angarya işleri de beraberinde
getiriyordu. EF ve benzeri araçlar angarya ve banal işleri mümkün olduğun-
ca otomatikleştirerek en aza indiren son derece faydalı yazılım araçlarıdır.
EF ile artık her bir proje için ayrı ayrı bütünKayıtlarıOku(), kayıtBul
(id), kayıtSil(id), güncelle(nesne) tarzı yardımcı yapılar yazmak-
tan büyük ölçüde kurtulmuş oluyoruz. Tabii ki EF abrakadabra ile yazılımla-
rımızın veri-erişim tabakasını tamamen otomatik olarak bizim için oluştura-
mayacaktır. Böyle bir beklenti içinde olmamız hem yanlış hem de kontrolü
olması gerektiğinden fazla bir miktarda EF’e bırakıyor olacağımızdan riskli
bir durum olacaktır. EF’in bizim için neler yapabileceğini ve daha da önemli-
si neleri iyi yapabildiğini ve neleri o kadar da iyi beceremediğini bilmek ve
anlamak sağlam yazılımlar üretebilmemiz için gereklidir. Uygulamalı olarak
EF’i yakından tanımak ve EF’den optimum verim almanın yollarını araştır-
mak için örnek bir proje ile EF’e giriş yapalım.
Örnek Proje: Visual Studio 2012, C#, EF5
F5Dergi.EF adında bir proje yaratalım ve projemize VT.sdf adında bir lokal
veri tabanı (Local Database) ekleyelim. Örnek projemizi tek bir parça olarak
paketleyebilmek ve sizlerle paylaşabilmek için lokal veri tabanı ekliyoruz.
Entity Framework
40
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Normalde pek tabii ki SQL Server, Oracle vb. gelişmiş veri tabanı ortamları-
na bağlantı oluşturulacaktır, ancak bu durum EF açısından bir farklılık ya-
ratmaz.
Projemize bir lokal veri tabanı ekledikten sonra aşağıdaki gibi tabloları ve
tablolar arasındaki ilişkileri oluşturalım:
VERİ TABANI YAPISAL DESEN
ÖNEMLİ NOT: Veri tabanımızdaki tabloların ID sütunları otomatik artan
Identity şeklinde tanılanmış sütunlardır.
Şimdi yukarıdaki şekliyle oluşturduğumuz tablolara birtakım örnek kayıtlar
girelim.
Müşteri Tablosu
Kategori Tablosu
Entity Framework
41
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Ürün Tablosu
Sipariş Tablosu
SiparişDetay Tablosu
ADO.NET Veri Modeli
Şimdi projemize VeriModeli.edmx adında bir veri modeli (ADO.NET Entity
Data Model) ekleyelim. Visual Studio bize modelin içeriğinin ne olacağını
sorduğunda Generate from database seçeneğini, bir sonraki adımda
VT.sdf adıyla yarattığımız lokal veri tabanımızı seçelim.
Entity Framework
42
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Bir sonraki adımda da veri tabanında tanımlanmış olan tablolarımızı işaret-
leyerek veri modelimizi oluşturacak üniteleri seçelim.
Bu işlem sırasında Visual Studio projemize bir takım referanslar ve VeriMo-
deli.edmx başlığı altında listelenen bir takım dosyalar ve referanslar ekler.
İlk etapta fark edilmesi gereken durum veri tabanımızda tanımladığımız her
bir tablo için bir .cs dosyası tanımlanmasıdır. Ve bu dosyaların içeriğine ba-
kacak olursak tabloya karşılık gelecek bir public sınıf, her tablo alanı için de
bir public alan tanımlandığını görebiliriz. Ayrıca bu sınıflar içinde tablolar
arasındaki ilişkilere bağlantılı olarak bir takım navigasyon alanları da ek-
Entity Framework
43
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
lenmiştir. Mesela Kategori.cs sınıfındaki ICollection<Ürün> Ürün alanı o
kategoriye ait ürünlere kolaylıkla erişebileceğimiz bir alan olarak Visual Stu-
dio tarafından otomatik olarak tanımlanmıştır. Bu otomatik işlem veri taba-
nımızda Kategori tablosundaki ID sütunu ile Ürün tablosundaki KategoriID
sütunu arasında tanımladığımız ilişki sayesinde gerçekleştirilir.
Visual Studio tarafından gerçekleştirilen bu otomatik işlemler İngilizce dili
için optimize edilmiş olduğundan veri tabanımızdaki tabloları temsil
eden .cs sınıflarına eklenen ilişkisel alanların bazılarında tekilden çoğula
olacak şekilde bir takım imla değişiklikleri yapmamız uygun olur. Visual Stu-
dio veri modelimiz üzerinde bu ve benzeri değişiklikler yapabileceğimiz bir
diyagram oluşturur. Bu diyagramı Solution Explorer altında VeriMode-
li.edmx dosyasına çift-tık yaparak açabiliriz. Bu diyagram veri tabanı yapı-
sal desenine benzemekle beraber aslında bizim için oluşturulan .NET sınıf-
larının ve bu sınıflar arasındaki ilişkilerin görsel bir temsilidir.
Bu diyagram aracılığıyla aşağıda da işaret edildiği gibi navigasyon alanları
üzerinde uygun imla değişiklikleri yapmak mümkündür ve yazacağımız kod-
ların anlaşılır olması adına tavsiye edilmektedir.
Veri modeli diyagramı
Navigasyon alanları üzerinde tekilden çoğula olacak şekilde yapılan imla
Entity Framework
44
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
değişikliklerinin yanı sıra her bir ünite için ayrıca Entity Set Name alanında
da tekilden çoğula isim değişikliklerimizi aşağıdaki gibi gerçekleştiriyoruz.
Benzeri Entity Set Name değişikliklerini diğer (Ürün, Müşteri, Sipariş,
SiparişDetay) üniteler üzerinde de gerçekleştiriyoruz.
Visual Studio tarafından projemize otomatik olarak eklenen dosyalar ara-
sında VeriModeli.Context.cs adlı bir dosya ve bu dosya içinde VTEnti-
ties adıyla tanımlanmış bir kısmi sınıf (partial class) mevcuttur. Bu sınıf
System.Data.Entity ad-alanı altında tanımlanmış olan DbContext sını-
fından kalıt almaktadır. DbContext sınıfı EF’in en önemli yapılarından biri-
dir. Bu sınıf bize .NET ortamından veri tabanıyla etkileşebileceğimiz bir
“bağlam” sağlayacaktır. Yani yukarıda bahsettiğimiz angarya işlerin çoğunu
bizim için üstlenecek yapı bu yapıdır.
EF’i kullanarak .NET ortamından (C#, VB.NET vs.) bu bağlam ve otomatik
olarak yaratılan model sınıfları aracılığıyla veri tabanıyla nasıl kolaylıkla et-
kileşebiliriz bunu örnek kodlarla inceleyelim şimdi.
Bağlam yaratma
Herşeyden önce Visual Studio tarafından bizim için otomatik olarak yaratı-
lan VTEntities sınıfı aracılığıyla bir bağlam nesnesi oluşturmalıyız. Veri
modelimizi oluştururken seçtiğimiz veri tabanına bağlanmak için gerekli bil-
giler app.config (web projeleri için web.config) dosyasına otomatik olarak
kaydedilmiştir. VTEntities sınıfının parametresiz yapıcı metodu ile kolay-
lıkla bağlam nesnemizi yaratabiliriz.
//veri tabanı bağlantı bilgileri .config dosyasında
VTEntities vt = new VTEntities(); //bağlam yarat
Entity Framework
45
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Eğer VTEntities sınıfına göz atacak olursak yapıcı metodunun aşağıdaki
gibi olduğunu görürüz:
public VTEntities()
: base("name=VTEntities") { }
Bu yapıcı metot kalıt aldığı sınıfın (DbContext) yapıcı metotlarından birini
"name=VTEntities" parametresi ile çağırmaktadır. Bu aslında app.config
dosyasına bizim için otomatik olarak eklenen bağlantı detaylarına bir refe-
ranstır.
App.config içindeki bağlantı bilgileri
<connectionStrings>
<add name="VTEntities" connectionString="metadata=res://*/VeriModeli.csdl|res://*/VeriModeli.ssdl|res://*/VeriModeli.msl;provider=System.Data.SqlServerCe.4.0;provider con-nection string="data source=|DataDirectory|\VT.sdf"" providerName="System.Data.EntityClient" /> </connectionStrings>
Burada vurgulanması gereken bir diğer nokta da otomatik olarak yaratılan
yapıların kısmi sınıflar olarak yaratılmasıdır. Bundan dolayı işlevsel olarak
kolaylıkla geliştirilebilirler. Mesela VTEntities kısmi sınıfına yeni ve daha
esnek bir yapıcı metot eklemek projemize aşağıdaki gibi bir kısmi sınıf
(partial class) eklemekle mümkün olacaktır.
public partial class VTEntities : DbContext { public VTEntities(string bağlantı) : base(bağlantı) { } public static VTEntities BağlamNesnesiAl() { var bağlam = new VTEntities(); return bağlam; } }
Bu ikinci kısmi VTEntities sınıfı bize bağlam yaratırken bağlantı bilgilerini
daha dinamik ve esnek bir biçimde kontrol edebilme imkanı sağlayacaktır.
#if DEBUG vt = new VTEntities("name=VTEntities_Test");
Entity Framework
46
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
#else vt = new VTEntities("name=VTEntities"); #endif Bağlam yaratma konusunu kapatmadan önce projelerimizin değişik yerle-
rinde kullanılacak bağlam nesnelerinin oluşturulmasını fabrika deseni gibi
bir desenle, ya da yukarıda tanımladığımız BağlamNesnesiAl statik meto-
du gibi bir metotla merkezi bir noktaya toplamak akıllıca olacaktır. Bu şekil-
de bağlam konfigürasyonunda veya bağlam nesnelerinin yaratılması işle-
minde değişikliğe gitmek istersek bu değişikliği kolaylıkla yapabiliriz.
Veri sorgulama Bağlam nesnelerinin nasıl yaratıldığını gördükten sonra şimdi veri tabanın-
dan EF ile nasıl veri okuruz bunu inceleyelim. Veri okuma işi sıklıkla bağ-
lam sınıfı üzerinde tanımlanmış navigasyon alanları aracılığıyla yapılır.
VTEntities vt = new VTEntities(); //bağlam yarat var kategoriler = vt.Kategoriler; foreach (var kategori in kategoriler) { Console.WriteLine("ID: " + kategori.ID + ", " + "Ad:" + kategori.Ad + ", " + "Ürün adet:" + kategori.Ürünler.Count().ToString()); }
Yukarıdaki kod haricinde başka bir kod yazmadan veri tabanından kayıt
okuyoruz ve birbiriyle ilişkili olan tablolar arasında (Kategori ve Ürün tablo-
ları) dinamik bir şekilde ilişkisel okuma yapabiliyoruz. Harika, değil mi?
Burada vt nesnesi üzerindeki Kategoriler alanı DbSet<Kategori> dön-
düren bir bağlam veri erişim alanı ve Kategori sınıfına ait Ürünler alanı da
ICollection<Ürün> döndüren bir ilişkisel veri erişim alandır. (Daha önce
Ürün olan bu alanı Ürünler olarak değiştirmiştik)
Veri tabanına kayıt yapma
EF ile veri tabanına veri yazdırmak da veri okumak kadar kolay! Bağlam
nesnesine ait veri erişim alanları üzerinden ulaşılan nesneler üzerinde di-
rekt değişiklik yaparak ve/veya DbSet<T> sınıfına ait Add (Ekle) ve Remove
(Çıkar/Sil) metotları aracılığıyla veri manipülasyonlarımızı gerçekleştirdikten
sonra DbContext sınıfına ait SaveChanges metodunu çağırarak veri tabanı-
na kayıt yapabiliriz. Bunu hemen çok basit bir şekilde örnekleyelim.
var kategoriler = vt.Kategoriler;
//yeni kategori ekle
Entity Framework
47
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Kategori yeniKategori = kategoriler.Add(
new Kategori { Ad = "Yeni kategori"});
vt.SaveChanges(); //bağlam aracılığıyla veri tabanına kaydet //yukarıdaki SaveChanges işlemi yeniKategori.ID alanını // otomatik olarak güncelleyecektir //KategoriID’si 1 olan ürünleri seç var ürünler = vt.Ürünler.Where(o => o.KategoriID == 1); foreach (var ürün in ürünler) ürün.KategoriID = yeniKategori.ID;//yeni kategoriye transfer et vt.SaveChanges(); //veri tabanına kaydet
Yukarıdaki kod parçası EF ile yeni tanışan okuyucularımız için gerçekten
abrakadabra gibi gözükebilir. Deneyin ve kodun sorunsuz çalıştığını, yeni
kaydın veri tabanına kaydedildiğini ve KategoriID’si 1 olan ürünlerin Kate-
goriID’lerinin yeni yaratılan kategorinin ID’si olarak değiştiğini göreceksiniz.
Neredeyse hiç kod yazmadan veri-erişim katmanımız hazır!
Şimdi yukarıdaki verdiğimiz örnek kodları yakından incelemek suretiyle EF’i
ve EF’i oluşturan unsurları biraz daha derinlemesine anlamaya çalışalım.
DbContext
Yukarıda da bahsettiğimiz gibi çok önemli bir sınıf olan DbContext bağlam-
larımızın kalıt aldığı ve veri tabanı ile olan etkileşim işinin asıl yapıldığı bir
sınıftır. Projemize otomatik olarak eklenen VTEntities sınıfına bakarsak,
ne kadar yalın bir sınıf olduğunu görebiliriz. Ancak DbContext sınıfının içeri-
ğini biraz inceleyecek olursak EF’in can damarına bakmakta olduğumuzu
fark etmek çok da zor olmaz.
Bu makalede EF’in iç çalışma prensiplerine çok fazla derinlemesine girerek
gereksiz bir karmaşıklık içinde bocalamaktansa özet bir bakış ile EF’i iyi an-
lamamız için gerekli alt yapıyı oluşturmak sanırız yeteli olacaktır. DbContext
sınıfı içinde yer alan şu yardımcı yapılar dikkati çekmektedir:
DbChangeTracker
DbContext bazlı bağlam nesneleri bu yapı aracılığıyla .NET nesneleri üze-
rindeki değişikliklerin takibini yapar. DbContext içinde yer alan mantık
DbChangeTracker sayesinde SaveChanges metodu çağırıldığında veri ta-
banına gönderilecek SQL cümlelerini tespit eder.
Entity Framework
48
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Database
Bağlantı yapılan veri tabanı ve bağlam yapıları arasında gerçeklesen iletişi-
mi yönetir.
DbContextConfiguration
DbContext bağlam nesnelerinin Configuration alanı üzerinden ulaşıp mani-
püle edebileceğimiz bu yapı bağlam nesnemizin veri tabanından veri yükle-
me işlemi sırasındaki karakteristikleri kontrol etmemize olanak sağlayan bir
yapıdır.
DbModelBuilder
Bu sınıf veri tabanındaki yapıların ve bu yapılar arasındaki ilişkilerin .NET
ortamında temsil ediliş şeklini yöneten bir yapıdır. Daha çok önce-kod
(code-first) disipliniyle geliştirilen yazılım projelerinde sıklıkla kullanılan bu
yapı klasik yöntem olan önce-veri tabanı (database-first) disiplininde de
faydalı olabilmektedir.
DbSet
DbContext sınıfından sonra en önemli ve EF projelerinde sıklıkla kullanılan
bu yapı liste bazlı (IEnumerable ve IQueryable ara-yüzünden kalıt alan) bir
yapı olup, DbContext sınıfımıza otomatik olarak yerleştirilen veri erişim
alanlarının dönüş tipi olduğundan kodlarımızda bizzat ve doğrudan kullan-
dığımız bir sınıftır. Bu nedenledir ki DbSet sınıfını ve sağladı işlevleri iyi an-
lamak EF’i verimli bir şekilde kullanabilmek için çok gereklidir.
VTEntities bağlam sınıfındaki veri erişim alanları
public DbSet<Kategori> Kategoriler { get; set; } public DbSet<Müşteri> Müşteriler { get; set; } public DbSet<Sipariş> Siparişler { get; set; } public DbSet<SiparişDetay> SiparişDetayları { get; set; } public DbSet<Ürün> Ürünler { get; set; }
Yukarıda listelenen veri erişim alanları aracılığıyla veri tabanından .NET
ortamına ve .NET ortamından veri tabanına kolaylıkla bilgi aktarabiliyoruz.
Peki nasıl oluyor da vt.Kategoriler şeklinde bir ifadeyle elimize
DbSet<Kategori> tipinde bir nesne geçiyor? Bunun cevabı bağlam sınıfı-
mızın kalıt aldığı yapı DbContext sınıfının içindedir.
VTEntities vt = new VTEntities(); ifadesiyle bir bağlam nesnesi ya-
rattığımızda VTEntities sınıfının yapıcı (constructor) metodu ve dolayısıyla
DbContext sınıfının yapıcı metodu çalışıyor. Bu işlem sırasında get ve set
Entity Framework
49
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
metotlarına sahip Kategoriler alanına bir değer atanıyor. Daha sonra
var kategoriler = vt.Kategoriler; şeklinde okuduğumuz bu değeri
yakından inceleyecek olursak içinde bir SQL cümlesi barındırdığını projemi-
ze uygun bir noktada breakpoint yerleştirip kategoriler.ToString() ifa-
desi aracılığıyla görüntüleyebiliriz.
DbSet<Kategori> yapısı içindeki SQL
SELECT \r\n[Extent1].[ID] AS [ID], \r\n[Extent1].[Ad] AS
[Ad]\r\nFROM [Kategori] AS [Extent1]
Bu SQL cümlesi EF tarafından otomatik olarak oluşturulur ve Kategoriler
alanından okuma yaptığımızda bu SQL cümlesi veri tabanı ortamına gön-
derilir ve sonuç veri kümesi EF tarafından DbSet<Kategori> tipine dönüş-
türülür. DbSet<T> sınıfı IQueryable, IEnumerable, IQueryable<T> ve IEnu-
merable<T> ifadelerinden kalıt alır ve bir önceki sayımızdaki Linq ve
Lambda İfadeleri adlı makalemizde incelediğimiz “gecikmeli yürütme” kav-
ramının geçerli olduğu bir yapıdır. DbSet’in bu özelliğini iyi anlamak EF’i iyi
anlamak için son derece mühimdir. Şimdi uygulamamız ile veri-tabanı ara-
sındaki iletişimi analiz ederek DbSet bazlı yapılarının çalışma prensiplerini
yakından tanıyalım.
Gecikmeli Yürütme (Deferred Execution)
Projemizde var kategoriler = vt.Kategoriler; ifadesiyle kategori-
ler değişkenine değer atıyoruz. Ancak, yürütmenin bu satırı çalıştırması
aşamasında veri tabanına henüz herhangi bir sorgu gönderilmez. Ne za-
man ki kategoriler değişkeninden değer okur ve bu değerleri kullanırız,
işte o zaman otomatik olarak oluşturulan SQL veri tabanına gönderilir ve
gerçek veri okuma işlemi gerçekleşir.
NOT: EF’in veri tabanını ne zaman gerçekten sorguladığı SQL Server
Profiler gibi araçlarla kolaylıkla gözlemlenebilir.
Yukarıda verdiğimiz veri sorgulama örneğimizde yürütmenin
foreach (var kategori in kategoriler) döngüsüne girmesiyle SQL
cümlesi veri tabanına gönderilir. Burada performans açısından dikkat edil-
mesi gereken bir konuya aşağıdaki örnekle vurgu yapalım:
DbSet<Kategori> kategoriler = vt.Kategoriler;
foreach (var kategori in kategoriler) //veri tabanı sorgulanır
{...}
foreach (var kategori in kategoriler) //veri tabanı YİNE sorgulanır !!! {...}
Entity Framework
50
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Burada ikinci döngü veri tabanının yeniden sorgulanmasına sebep olacak-
tır. Evet, yanlış okumadınız! Gerçekten de kategoriler değişkenimize
vt.Kategoriler ifadesiyle ile değer atayıp bir kere kullanmamıza rağmen
yürütme ikinci döngüye girdiğinde EF veri tabanını yeniden sorgular! Bu ço-
ğu zaman gereksiz bir sorgulama olacaktır. Uygulamamız ile veri tabanı
arasındaki iletişimde ortaya çıkabilecek bu tip gereksiz trafiği engellemek
için IEnumerable<T> yapısına ait olan ToList, ToArray, ToDictionary, ToLo-
okup eklenti metotlarını kullanarak EF’i veri tabanını derhal sorgulamaya
zorlayarak sonuç veri kümesini belleğe sabitleyerek veri tabanının birden
çok kez sorgulanmasına mani olabiliriz. Yani yukarıdaki kodu şu şekilde
yazmamız daha doğru olacaktır:
//veri tabanı bir defa sorgulanır ve sonuç belleğe derhal yerleştirilir List<Kategori> kategoriler = vt.Kategoriler.ToList(); foreach (var kategori in kategoriler) //veri bellekteki listeden okunur {...}
foreach (var kategori in kategoriler) //veri bellekteki listeden okunur {...}
Veriyi belleğe derhal yerleştiren bu teknik dikkatle kullanılması ve iyi öngörü
gerektiren bir tekniktir. Yukarıdaki örnekteki gibi bazı durumlarda .ToList
metodu ile veriyi belleğe yerleştirmek performans açısından mutlaka yapıl-
ması gereken bir durum olmakla birlikte, bazı durumlarda da bu yöntemin
çok erken kullanımı büyük bir performans düşmanı haline gelebilmektedir.
Şimdi de bu durumu bir örnekle inceleyelim.
//veri tabanı sorgulanır ve bütün siparişler belleğe yerleştirilir
List<Sipariş> siparişler = vt.Siparişler.ToList();
//müşteriID ile filtrele
var müşteriSiparişleri =
siparişler.Where(s=>s.MüşteriID == müşteriID);
foreach (var sipariş in müşteriSiparişleri) {...}
Bu kod parçasındaki problem vt.Siparişler.ToList() ifadesiyle veri ta-
banında tutulan bütün sipariş kayıtlarının gereksiz olarak belleğe yüklenme-
sidir. Eğer yazmakta olduğumuz program mantığı çerçevesinde bize sade-
ce tek bir müşteriye ait siparişler gerekiyorsa bütün siparişleri belleğe yükle-
mek performans açısından son derece yanlış bir yaklaşım olacaktır. Böyle
bir durumda ideal olarak veri tabanına gönderilecek sorgunun WHERE
Entity Framework
51
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Müşteri.MüşteriID = müşteriID şeklinde bir filtre içermesi gerekir. Bu soru-
nu bir önceki sayımızda incelediğimiz Lambda İfadelerinin IQueryable ya-
pılar üzerine uygulanması yöntemiyle rahatlıkla halledebiliriz.
//filtre içeren veri tabanı sorgusu oluşturuluyor
// ancak yürütme geciktiriliyor (dönüş tipi IQueryable)
IQueryable<Sipariş> müşteriSiparişleri = vt.Siparişler
.Where(s=>s.MüşteriID == müşteriID);
foreach (var sipariş in müşteriSiparişleri) //veri tabanı filtre {...} // ile sorgulanır
Yukarıdaki örnekte müşteriSiparişleri.ToString() ifadesi ile görüntü-
lenecek SQL cümlesi şu şekilde olacaktır:
SELECT [Extent1].[ID] AS [ID], [Extent1].[MüşteriID] AS [MüşteriID],
[Extent1].[Tarih] AS [Tarih]
FROM [Sipariş] AS [Extent1]
WHERE [Extent1].[MüşteriID] = @p__linq__0
NOT: @p__linq__0 ifadesi asıl sorgu esnasında müşteriID değişkeninde tutulan
değer ile değiştirilecektir.
Bu örnekte veri tabanından gereksiz bir şekilde bütün siparişleri sorgulama
problemini çözmekle beraber müşteriSiparişleri ifadesinin bir IQuer-
yable olmasından dolayı müşteriSiparişleri ifadesi her kullanıldığında
veri tabanı sorgulanacaktır. Bir önceki örneğimizdeki çözümü burada da
aşağıdaki şekilde uygulayarak bu sorunu da halledebiliriz.
IQueryable<Sipariş> müşteriSiparişleri = vt.Siparişler
.Where(s=>s.MüşteriID == müşteriID)
.ToList();
//veri tabanı filtre ile sorgulanır ve sonuç belleğe yerleştirilir
Yürütmeyi ne zaman geciktirmeli?
Değişik örneklerle veri tabanından gelecek olan veriyi gerçek anlamda bel-
leğe yerleştirmek veya IQueryable olarak bırakmak (yani yürütmeyi geciktir-
mek) konusu duruma, kişiye, projeye göre değişik cevapları olan bir ikilem-
dir.
Az kullanıcılı ve belleğin “pahalı” olmadığı projelerde belki de pek fazla fark
Entity Framework
52
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
yaratmayacak bu durum, yüksek volümlü ve/veya eş zamanlı (real time)
sistemlerde büyük bir dikkatle çözüm aranması gereken bir problemdir.
Genel kural olarak projelerin alt yapı katmanlarında yer alan veri erişim me-
totları için IQueryable<T> tipinde dönüş tipleri daha uygun olacaktır. Üst
yapılarda yer alan ve veri okumadan ziyade veri işleyen mekanizmalarda
ToList, ToArray vb. metotlar ile özellikle sık kullanılan ve az değişen veri
kümeleri belleğe yerleştirilmelidir.
Bu yaklaşımın başlıca sebebi IQueryable olarak nesneler ve metotlar ara-
sında gidip gelen parametreler istenilen bir zamanda (ToList, ToArray vb.
metotlarla) veya dolaylı olarak (mesela döngü içinde kullanmak yoluyla)
belleğe aktarılabilirler. Ancak pek tabii ki bunun tersi geçerli değildir, yani
yürütme bir kere gerçekleştikten sonra bunun geri dönüşü yok!
Aslında IQueryable<T> döndürmek ile IEnumerable<T> döndürmek arasın-
da pek bir fark olmadığı söylenebilir. IEnumerable<T> döndüren metotlar
tek başlarına test edildiklerinde IQueryable<T> döndüren metotlarla benzer
sonuçlar verebilmektedir. Ancak IEnumerable<T> ve IQueryable<T> ara-
sında önemli bir fark vardır.
Bu farkı somut bir örnekle görebilmek
için şöyle bir senaryo düşünelim. Son
12 ayda gerçekleşmiş olan ve 500 li-
ranın üzerinde sipariş veren müşteri-
leri tespit etmek isteyelim, ve bu ama-
cımızı gerçekleştirmek için aşağıdaki
yardımcı ekleme metotları ele alalım.
public static IEnumerable<Sipariş> Son12AyınYüksekSiparişleri(this IQueryable<Sipariş> kaynak) { var onİkiAyÖnce = DateTime.Today.AddMonths(-12); return kaynak.Where( o => o.Tarih >= onİkiAyÖnce && o.SiparişDetaylar.Sum(sd=>sd.ÜrünFiyat) >= 500 ); }
public static IEnumerable<Müşteri> IDİleSeç(this IQueryable<Müşteri> kaynak, IQueryable<int> idler) { return kaynak.Where(o => idler.Contains(o.ID)); }
Entity Framework
F5 Makale:
C# Ekleme (extension)
Metotları-http://
f5dergi.com/Makale/a/12
53
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
IQueryable<Sipariş> ve IQueryable<Müşteri> yapılarına eklediğimiz
bu ekleme metotlarını şu şekilde kullanabiliriz:
IQueryable<int> müşteriIDleri = vt.Siparişler
.Son12AyınYüksekSiparişleri()
.Select(s => s.MüşteriID); //yansıtma
var müşteriler = vt.Müşteriler.IDİleSeç(müşteriIDleri) .ToList(); //belleğe aktar foreach (var müş in müşteriler) {//2 ayrı SQL cümlesi veri Console.WriteLine(müş.Ad); // tabanına gönderilir }
Öncelikle şunu belirtelim: ekleme metotlara soyutladığımız Son12AyınYük-
sekSiparişleri ve IDİleSeç işlevleri kodumuzun çok daha anlaşılır ve
okunaklı olmasını sağlamaktadır.
Ancak burada esas önemli ve yanlış olan nokta bu metotların IEnumerab-
le<T> döndürmesidir. Daha öncede belirttiğimiz gibi tek başlarına test edilir-
lerse dönüş tipinin IEnumerable<T> ve IQueryable<T> olması arasında
fark olmayacaktır. Fakat yukarıdaki örneğimizde yer alan foreach döngüsü
iki farklı SQL sorgusu veri tabanına gönderecektir. Birinci sorgu Sipariş tab-
losundan ilgili MüşteriID’lerini okumak için, ikinci sorgu da Müşteri tablosun-
dan ilgili müşterileri seçmek için olacaktır. Oysa metotlarımızı IQueryab-
le<T> döndürecek şekilde yazarsak aynı foreach döngüsü veri tabanına iki
sorguyu tek bir bileşik sorgu olarak gönderecektir. Bu da performans açı-
sından çok daha tercih edilen bir durum olacaktır.
Ekleme metotlarıyla oluşturduğumuz veri sorgulama metotlarıyla beraber
önemli bir diğer not olarak şunu da vurgulamamız gerekir: Ekleme metotları
mutlak suretle IQueryable<T> yapılarına eklenmelidir. Eğer yukarıdaki me-
totları ... Son12AyınYüksekSiparişleri(this IEnumerable<Sipariş>
kaynak)... ve ...IDİleSeç(this IEnumerable<Müşteri> kaynak, ...
şeklinde yazarsak bu metot her sorgulandığında SELECT * FROM
[Müşteriler] sorgusu veri tabanına gönderilecektir. Bu veri son derece yan-
lış bir durum olacaktır. O yüzden EF içerisinde yazacağımız ekleme metot-
ları mutlaka IQueryable<T> yapılarına eklenerek yazılmalıdır.
Örneğimizdeki Son12AyınYüksekSiparişleri ve IDİleSeç ekleme me-
totları gibi veri sorgulama metotlarının IQueryable<T> yapılarına eklenmesi
Entity Framework
54
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
List ve dizi gibi yapılar tarafından kullanılamayacağı olması anlamına gel-
mez. Aşağıda da görülebileceği gibi .AsQueryable metodu ile liste ve dizi
bazlı yapıları IQueryable<T> tiplerine döndürülebilir.
var lstSipariş = vt.Siparişler.ToList();
var lstMüşteriler = vt.Müşteriler.ToList();
//liste kolaylıkla IQueryable’a dönüötürülebilir
var müşteriIDleri = lstSipariş.AsQueryable()
.Son12AyınYüksekSiparişleri()
.Select(s => s.MüşteriID); //yansıtma
var müşteriler = lstMüşteriler.AsQueryable()
.IDİleSeç(müşteriIDleri)
.ToList();
Entity Framework teknolojisini yakından tanıtmak istediğimiz bu makalemizi
sonlandırırken bir kez daha vurgulamak isteriz ki, EF yazılımcılara veri ta-
banından veri okuma ve veri manipülasyonu konularında son derece fayda-
lı olabilmekle birlikte EF ve veri tabanı arasındaki iletişimin nasıl olduğunu
anlamak EF’den tam anlamıyla faydalanabilmek ve sıkça karşılaşılan per-
formans hatalarını önlemek için oldukça gerekli bir durumdur.
EF geniş ve sürekli gelişen bir tek-
nolojidir, ve versiyon 4’ten itibaren
önceki versiyonlarından oldukça
farklıdır. Bu makaleyi Entity Fra-
mework’e bir giriş ve alt yapı sağla-
ması maksadıyla hazırlarken her
zaman olduğu gibi konuya hakim
olan olmayan bütün yazılımcı arka-
daşlara bir katkı sağlayacak şekilde
hazırlamaya çalıştık.
Yorum, istek ve düşüncelerinizi lütfen bizimle paylaşın.
http://f5dergi.com/Iletisim veya [email protected]
Entity Framework
Örnek Proje
http://f5dergi.com/indir/
Kod/ef.zip
(Üyelik gerektirmektedir)
Platform ve Teknolojiler
Visual Studio 2012,
Entity Framework 5
55
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
http://f5dergi.com
Yazılım makaleleri,
örnek kaynak kodlar,
sektörden haberler...
56
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
HTML5
Çevrim dışı (offline) çalışabilen web uygulamaları
Geçtiğimiz sayımızda yer alan jQuery Mobil makalemizde çevrim dışı çalı-
şabilen HTML5 uygulamaları konusundan bahsetmiştik. Bu makalemizi
HTML5 uyumlu tarayıcılar tarafından desteklenen bir takım özellikler yardı-
mıyla nasıl çevrim dışı da çalışabilen web uygulamaları geliştirilebileceği
konusuna ayırdık.
Web ve internetin doğası gereği, çevrim içi (online) olmak web uygulama-
larının çalışabilmesi için en başta gelen koşuldu az bir zaman öncesine ka-
dar. Ancak internet erişimli telefon ve tablet gibi cihazlarının artan kullanımı
ve bu taşınabilir cihazların çevrim dışı olduğu zamanlarda da kısmen de
olsa işlevsel olması artık kullanıcılar tarafından beklenmekte ve talep edil-
mektedir. Google Mail ve Financial Times gibi web uygulamaları HTML5
teknolojileriyle geliştirilirmiş ve çevrim dışı da çalışabilen uygulamalardır.
Financial Times - çevrim dışı ekran çıktısı
Yukarıdaki ekran çıktısı internet bağlantısı kesilmiş olan bir iPad cihazından
alınmıştır. Burada da görüldüğü gibi http://app.ft.com adresi üzerinden ulaş-
tığımız FT HTML5 uygulaması internet bağlantısı mevcutken indirilen ve
C# ile LINQ ve Lambda İfadeleri
57
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
belleğe yerleştirilen veri ve dosyaların çevrim dışı olunan durumda kullanıl-
masıyla internet bağlantısı mevcut değilse bile kısmi işlev sunabiliyor. Bu
makale ile nasıl çevrim dışı da çalışabilen web uygulamaları geliştirebiliriz
bunu inceleyeceğiz.
ÖNEMLİ NOT: Çevrim dışı çalışabilme özelliği bütün tarayıcılar tarafından
desteklenmemektedir. Akıllı telefon ve tablet gibi cihazların çoğunda ve ma-
saüstü tarayıcılardan da Firefox, Chrome, Safari ve Opera tarayıcılarının
son versiyonları tarafından desteklenen bu özellik Internet Explorer 10 tara-
fından desteklenecektir.
Bellek Bildirim
(Cache Manifest)
Dosyası Özümsenmesi gereken ilk
unsur olan bu dosya özel bir
bildirim dosyası olup HTML5
uygulamalarının çevrim dışı
çalışabilmesi için hangi dos-
yaların tarayıcının belleğine
kaydedileceğini dikte eder.
Genellikle uygulamanın ana sayfası gibi ilk yüklenecek olan dosya içinde
<html> bloğunda aşağıdaki şekilde referans edilir:
<html manifest="bellek.appcache">
Yukarıdaki ifadeyle tarayıcıya bellek.appcache dosyasının bir bellek bildi-
rim dosyası olduğunu söylemiş oluyoruz. .appcache sektörde kabul gör-
müş ve tavsiye edilen dosya uzantı adı olmakla birlikte dosya adı ve uzantı-
sı istediğimiz her hangi bir değer olabilir. Ancak seçtiğimiz uzantının web
sunucumuz tarafından text/cache-manifest MIME tipiyle servis edilmesi
gerekmektedir.
Bir diğer önemli konu ise bu bellek bildirim dosyalarının CACHE MANIFEST
ifadesiyle başlaması gerekliliğidir. Bu başlangıcın ardından gelecek olan
içerik 3 bölümden oluşur.
NOT: Bellek bildirim dosyası içine işlevsel olmayan not ve açıklamalar # karakteri
ile başlayan satırlara yerleştirilebilir.
C# ile LINQ ve Lambda İfadeleri
Örnek HTML Sayfa
http://lab.f5dergi.com/201211/
html5/anaSayfa.html
Bu sayfayı tarayıcınızda görün-
tüleyin ve hiçbir linke basmadan
tarayıcınızı kapatın. İnternet
bağlantınızı kesin. Sayfayı ye-
niden açın ve örnek uygulamamızı
çevrim dış durumda kullanın!
58
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
1. CACHE
Bu bölüm tarayıcıya hangi dosyaların çevrim dışı kullanılmak üzere belleğe
yerleştirmesi gerektiğini dikte eder. Bellek bildirim dosyalarına referans içe-
ren dosyalar, yani <html> bloğunda manifest alanı içeren dosyalar, tarayıcı-
lar tarafından otomatik olarak belleğe yerleştirildiklerinden CACHE bölü-
münde listelenmelerine gerek yoktur.
# Yorumlar # işaretiyle başlayan satırlara girilebilir
CACHE:
css/ana.css
grafik/logo.gif
grafik/arkaPlan.png
js/ana.js
index.html
çevrimDisi.html
Bellek bildirim dosyaları içinde her hangi bir başlık altında olmayan ifadeler
CACHE bölümündelermiş gibi kabul edilirler.
2. FALLBACK
Bu bölüm çevrim dışıyken ziyaret edilmek istenen sayfaların bellekte mev-
cut olmamaları halinde görüntülenecek alternatif sayfa veya sayfaların be-
lirlenmesi için kullanılır. Mesela, aşağıdaki / /çevrimDisi.html ifadesi
bellekte bulunmayan herhangi bir sayfanın istenmesi halinde çevrimDi-
si.html sayfasının gösterilmesini dikte etmektedir.
# bellekte yoksa /çevrimDisi.html sayfasını göster
FALLBACK:
/ /çevrimDisi.html
3. NETWORK
Bu bölümde de mutlak suretle sunucudan okunması gereken dosyaların
bildirimi yapılır. Burada listelenen dosyalar internet bağlantısı mevcut olsun
olmasın bellekten değil sunucudan talep edilecektir. Bağlantı mevcut değil-
se FALLBACK bölümünde ilgili sayfa için tanımlanan alternatif sayfa ya da
hata sayfası görüntülenecektir.
NETWORK:
kayıt.aspx
oturumAc.aspx
C# ile LINQ ve Lambda İfadeleri
59
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Konuyu daha detaylı incelemeye devam etmeden önce siz okuyucularımız
için hazırladığımız çevrim dışı çalışabilen örnek web uygulamamızı ziyaret
etmenizi tavsiye ederiz.
http://lab.f5dergi.com/201211/html5/anaSayfa.html adresinden erişebilece-
ğiniz bu basit örnek uygulamanın ana sayfası (anaSayfa.html) bir bellek bil-
dirim dosyası kullanarak uygulama içeriğinin büyük bir bölümünü yerel bel-
leğe kayıt edecektir. Başka hiçbir sayfaya erişmeden internet bağlantınızı
kesip aynı sayfaya yeniden erişirseniz göreceksiniz ki daha önce hiç ziyaret
etmediğiniz sayfalar bile erişilebilir olacaktır.
Bildirim dosyaları aracılığıyla belleğe yerleştirilen dosyalar, yani CACHE
bölümünde tanımlanan dosyalar, bir kere belleğe kaydedildikten sonra sü-
rekli bellekten servis edilirler. Bu bazı durumlarda istenmeyen bir durum
olacaktır. Mesela, belleğe yüklenen sayfalardaki içerik sunucudaki değişik-
likleri doğal olarak içermeyecektir. Bu da belleğe yerleşen dosyalar üzerin-
de bir takım kontrol ve yönetim gerekliliğini berberinde getirir.
Bunun tek yolu bellek bildirim dosyasında bir değişiklik yaparak bu dosya-
nın yeniden işlem görmesini sağlamaktır. Bellek bildirim dosyaları yeniden
işlem gördüklerinde CACHE bölümünde bildirilen dosyaların hepsi yeniden
sunucudan okunacak ve belleğe yeniden yerleştirileceklerdir. Bellek bildirim
dosyalarında # karakteri ile başlayan bir açıklama satırı içinde versiyon bil-
gisi ile bu işlemi yerine getirmek mümkündür. Yukarıdaki örnek sayfa tara-
fından kullanılan bildirim dosyasında bu teknik kullanılmaktadır.
CACHE MANIFEST
# versiyon 1.2 CACHE: grafik/logo.png css/ana.css js/jquery.js js/ana.js bilgi.html makaleler.html makaleler/makale1.html makaleler/makale2.html #...diğer makaleler...# çevrimDisi.html
FALLBACK: / çevrimDisi.html NETWORK: kayit.html
C# ile LINQ ve Lambda İfadeleri
60
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Burada şu önemli notu da düşmemiz gerekmektedir: bildirim dosyaları da
bizim arzumuz ve kontrolümüz dışında bazı tarayıcılar tarafından belleğe
yerleştirilebilir. Bu durum çevrim dışı çalışabilen HTML uygulamaları gelişti-
ren yazılımcılar için problem bir noktadır. Bu yüzden web sunucularında ge-
rekli konfigürasyon ile bellek bildirim dosyalarının asla tarayıcı belleğine
yerleşmemesi sağlanmalıdır.
Bir diğer önemli nokta da bellek bildirim dosyalarının asla kendilerini içer-
memeleridir. Eğer bir bellek bildirim dosyası CACHE bölümünde kendini
içerirse belleğin yenilenmesi neredeyse imkansız hale gelecektir. Bir başka
can sıkıcı durum da büyük ve küçük harflerle yazılmış dosya adları dosya-
nın farklı dosyalarmış gibi muamele görecek olmalarıdır. Eğer bildirim dos-
yamızın CACHE bölümünde anaSayfa.html sayfasını belleğe yerleştirilmek
üzere bildirmişsek uygulamamız içindeki bütün linkler anaSayfa.html şeklin-
de olmalıdır. Çevrim dışıyken anasayfa.html adresine yapılacak talepler
bellekte mevcut anaSayfa.html ile karşılanmayacaklardır. Bu da akılda tut-
mamız gereken bir husustur.
Bellek bildirim dosyaları ASPX, PHP vs. gibi sunucu teknolojileri ile de dina-
mik bir şekilde oluşturulabilir. Bu şekilde oluşturulan bellek bildirim dosyala-
rı Content-Type: text/cache-manifest şeklinde bir HTML Header ifadesi
içermelidir.
Bildirim dosyalarında yapılacak değişiklerin bellekteki içeriğin tazeleneceği
anlamına geldiğini söyledik. Ancak burada bilinmesi gereken önemli bir ay-
rıntı vardır. Bu da bellekteki içerik yenilendikten sonra bu yeni içeriğin he-
men değil bir sonra ki yüklemede etkin duruma geleceğidir. Bunu daha so-
mut bir senaryo ile açıklamaya çalışalım:
Farz edelim ki bildirim dosyasının CACHE bölümünde bir CSS dosyasının
belleğe kopyalanması bildirimini yaptık. Böylece çevrim dışı olunan durum-
larda da sayfalarımıza gerekli görselliği uygulayabilelim. Bu CSS dosyasın-
da bir değişiklik yaptığımızda doğal olarak ilk fırsatta bellekteki dosyayı ye-
nisiyle değiştirmek isteyeceğiz. Bu yüzden bellek bildirim dosyamızdaki ver-
siyon satırında bir değişiklik yaparak tarayıcılara bellekteki dosyaları yenile-
melerini dikte etmeliyiz. Tarayıcılar bellekteki dosyaları yenilerler ancak
sayfa zaten yüklenmiş olduğu için bu yenilikler bir sonraki yüklemede etkin
hale geleceklerdir. Bu durumu aşağıdaki JavaScript tekniği ile elimine ede-
biliriz.
function bellekGuncelle(e) { alert("Bellek güncelleniyor"); window.location.reload(); //sayfayı yeniden yükle
C# ile LINQ ve Lambda İfadeleri
61
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
} window.applicationCache.addEventListener('updateready',bellekGuncelle,false); Burada tarayıcı belleğinde bellek bildirim dosyamız için ayrılan alanı temsil
eden window.applicationCache nesnesinin yayınladığı updateready
olayına bellekGuncelle metodumuzu ekliyoruz ki tarayıcı tarafından oto-
matik olarak gerçekleştirilecek olan bellek yenileme işleminden hemen son-
ra sayfamızı yeniden yükleyerek değişikliklerin derhal etkin olmasını sağla-
mış oluyoruz.
Sadece JavaScript ile bellekteki içeriği manipüle etmemiz mümkün değil
ancak window.applicationCache.update() ifadesi ile tarayıcının bellek
bildirimi (manifest) dosyasını kontrol etmesini ve bu dosyada bir değişiklik
tespit edilirse bellek içeriği window.applicationCache.swapCache() ile
yeniden yüklemeye sebep olmaksızın yenilenebilir. update() ve swapCache
() metotları üzerinde bulunulan sayfa tarafından kullanılan JavaScript, XML
vs. gibi dosyaların bellekte tazelenmesi gibi senaryolarda faydalı olabilir. Bir
çok blog ve web sitelerinde yanlış bir şekilde ifade edildiğinin aksine win-
dow.applicationCache.swapCache() ve window.location.reload()
ifadelerini arka arkaya çalıştırmak gereksizdir. window.location.reload
() ile sayfanın yeniden yüklenmesi zaten bellek içeriğini tazeleyecektir.
Makalemizi sonlandırırken şunu da belirtelim: çevrim dışı çalışabilen uygu-
lamalar geliştirmede kullanılan bellek bildirim dosyaları ve win-
dow.applicationCache nesnesi üzerinden yapılan JavaScript manipülas-
yonlarının diğer normal web uygulamalarında da kullanılarak çevrim içi olu-
nan durumlarda hatırı sayılır performans kazanımları elde edebiliriz. Bellek
bildirim dosyalarında bildirilen kaynakların yerel ortama indirilip belleğe yer-
leştirilme işlemi arka planda gerçekleşen bir işlem olduğundan bu teknikle
site ve web uygulamalarımızda yer kullanılan grafik, JavaScript, CSS,
JSON, XML vs. gibi dosyalar bellek bildirim dosyaları aracılığıyla önceden
tarayıcı belleğine yerleştirilebilirler. Böylece bu dosyalar ihtiyaç halinde su-
nucudan değil bellekten servis edilecekleri için gözle görünür performans
iyileşmeleri elde edilebilir. Bu tekniği performansın çok önemli olduğu proje-
ler üzerinde çalışan meslektaşlarımıza önemle tavsiye ederiz.
Okuyucularımızdan önemi her geçen gün artan HTML5 ve benzeri konular-
da deneyimlerinizi bizlerle paylaşmanızı rica ederiz.
http://f5dergi.com/Iletisim veya [email protected]
C# ile LINQ ve Lambda İfadeleri
62
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
Birim Testler
Geçtiğimiz sayımızı takip eden okuyucularımızın da hatırlayacağı gibi
ASP.NET MVC makalemizde model bağlama konusunun yanı sıra aksiyon
metotlarımızın test edilebilirliği ve bunun neden önemli olduğu konularını
incelemiştik.
Bazı okuyucularımız birim testler ve test edilebilirlik kavramlarının neden
önemli konular olduğunu incelememizi talep ettiler. Biz de bu makaleyle bu
talepleri karşılamak istedik.
Başlangıcı 1970’lere kadar uzanan birim test kavramı kimilerinin çok külfetli
bir iş olarak gördüğü ancak son yıllarda kimilerinin vazgeçilmezi haline gel-
miş olan bu disiplin, kullanmasa da her yazılımcının en azından teorik ola-
rak bilmesi gereken bir konudur.
Yazılım projelerinin ana koda paralel olarak yazılan birim testlerle destek-
lenmesi kod kalitesi ve sürdürülebilirliği arttırmayı ve amaçlayan bir di-
siplindir. Birim testler özellikle büyük ekipler tarafından geliştirilen yazılım
projelerinde kod kalitesini arttırmalarının yanında yazılımcıların başkaları
tarafından yazılan kodları anlamalarını büyük ölçüde kolaylaştıran bir nevi
açıklayıcı dokümantasyon görevi görürler.
Birim testlerin beraberinde getirdiği külfetlere rağmen yazılmalarının başlıca
sebebi proje ilerledikçe kod üzerinde yapılan değişikliklerin projenin diğer
kısımlarında beklenmedik bir yan etki yaratmamasıdır. Ekipteki yazılımcılar
tarafından değişiklikler yapıldıkça projeye ait birim testler çalıştırılır ve eğer
negatif sonuç veren birim testler varsa bunlar derhal mercek altına alınır ve
problem çözülerek birim testler yine çalıştırılır. Bu döngü bütün testler pozi-
tif sonuç verene kadar uygulanır.
Bu disiplinle geliştirilen projeler de birim testler sıklıkla çalıştırılacağından
problemler çok daha erken ve tamir edilmesi çok daha kolayken fark edi-
lirler.
Birim test nedir?
MANTIK içeren bir kod parçasının doğru çalışıp çalışmadığını kontrol
etmek amacıyla yazılmış kod birim testtir.
Örnek olarak aşağıdaki XML metninden bilgi ayıklama işleminde kullanaca-
63
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
ğımız bir C# sınıfı düşünelim. Örnek XML: <Konfig Sunucu="f5dergi.com" Protokol="https" Port="443"> </Konfig> public class KonfigOku { private XDocument xml = null; public KonfigOku(string xml) { try { this.xml = XDocument.Parse(xml); } catch (Exception e) { throw new Exception("Xml metni okunamıyor.", e); } } public string Sunucu { get { if (xml.Root.Attribute("Sunucu") != null) return xml.Root.Attribute("Sunucu").Value; throw new Exception("Sunucu okunamıyor."); } } }
KonfigOku adıyla yarattığımız sınıfta mantık içeren iki kısım var: yapıcı
(constructor) metot ve Sunucu alanı.
Bu demek oluyor ki bu sınıf için en az iki birim test yazmalıyız. Şimdi bu
sınıf için yazılabilecek şu birim testleri inceleyelim:
Birim Test 1: public bool KonfigOkuYapıcıMetotXmlMetniOkunamıyor() { try {//özellikle kötü xml ile dene KonfigOku konfigOku = new KonfigOku(""); return false; } catch (Exception e) {//gelecek hatayı kontrol et return ("Xml metni okunamıyor." == e.Message); } }
Bu birim test yapıcı metodu test etmekle sorumlu olup test metodun yanlış
formatta XML metni gönderilmesi durumunda bilinçli olarak hataya düşecek
64
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
olmasını ve Exception.Message alanındaki hata mesajını test etmektedir.
Test metodumuz beklenen hata gerçekleşirse true beklenen hata gerçek-
leşmezse false döndürecektir.
Birim Test 2 public bool KonfigOkuSunucuAlanındanBaşarılıOkumaYap() { KonfigOku konfigOku = new KonfigOku("<Konfig Sunu-cu=\"f5dergi.com\" Protokol=\"https\" Port=\"443\"></Konfig>"); //özelli return (konfigOku.Sunucu == "f5dergi.com"); }
İkinci birim testimiz doğru XML ile çağırılacak yapıcı metodun akabinde Su-
nucu alanından okunan değerin doğruluğunu tespit etmektedir.
Birim Test 3 public bool KonfigOkuSunucuAlanındanBaşarısızOkumaYap() { KonfigOku konfigOku = new KonfigOku("<Konfig Protokol=\"https\" Port=\"443\"></Konfig>"); try { string s = konfigOku.Sunucu; return false; } catch (Exception e) { return ("Sunucu okunamıyor." == e.Message); } }
Üçüncü birim testimiz imla olarak doğru ancak Sunucu bilgisi içermeyen bir
XML kullanıldığında Sunucu alanından dönecek hatayı doğruluyor. Birinci
test gibi beklenen hata gerçekleşirse testimiz başarılı aksi halde başarısız
olmuş olacak.
Üç birim test ile kapladığımız KonfigOku sınıfı ilerde kod üzerinde yapılacak
değişikliklere karşı korunmalı bir halde artık. KonfigOku sınıfındaki işlev ve
mantığa bağımlı olarak geliştirilecek kodlar bu birim testler pozitif sonuç
verdiği müddetçe çalışacaktır.
Farz edelim ki bu sınıf üzerindeki Sunucu alanından okuma yapan bir kod
parçası yazdık. Bu kod parçasında Sunucu alanından fırlatılacak hatayı
(Exception) yakalayıp alternatif bir işlem yaptığımızı düşünelim. Günler hat-
ta haftalar sonra ekibimizdeki başka bir programcının kendi gereksinimleri-
ne paralel olarak Sunucu alanında Exception fırlattığımız satırı return
65
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
null; olarak değiştirdiğinde KonfigOkuSunucuAlanındanBaşarısızOku-
maYap adlı birim test negatif bir sonuç verecek ve buna sebep olan prog-
ramcıyı uyaracaktır. Birim testlerin olmadığı ortamlarda bu tip kazaların ve
doğuracağı problemlerin varlığı çok daha geç ortaya çıkacak ve tespiti çok
daha uzun zaman alacaktır.
Somut bir biçimde asıl kodun birim testlerle desteklenmesini ve bunun kod
üzerinde nasıl koruyucu bir etkisi olduğunu görmüş olduk. Ancak bu örneği-
mizde dikkat ederseniz birim testlerimizi normal koddan farklı bir şekilde
yazmadık. Birim testlerin hakiki anlamda faydalı olabilmeleri için bir takım
özellikleri sağlaması beklenir. Birim testler:
1. Tek bir işlevi net bir şekilde test etmelidir
Bir birim test metodu içinde birden fazla işlev test edilmemelidir. Adı
üstünde, birim testler test edebilecekleri en küçük birimi test etmelidir-
ler.
2. Otomatik olarak çalışabilmelidir
Birim testleri çalıştırmak ve sonucunu almak karmaşık olmayan ve dı-
şarıdan müdahale gerektirmeyen bir işlem olmalıdır. Bir tık ile projeye
ait bütün birim testler çalıştırılabilmelidir. BAŞLAT komutu haricinde
veri girişi gerektirmeyecek şekilde yazılmalıdırlar.
3. Az bir zaman zarfında çalışıp sonuçlanmalıdır
Birim testler mümkün olan en kısa zamanda çalışıp sonuçlanacak şe-
kilde yazılmalıdır ki projeye ait bütün birim testler saatler almamalıdır.
Projeye ait birim testler ne kadar uzun sürerse o kadar az çalıştırıla-
caklarından hataların çabuk fark edilmesinde olumsuz bir etki yarata-
caktır.
4. Geçerliğini kaybetmemelidirler
Birim testler proje üzerindeki değişiklerden etkilenmeyecek bir şekilde
yazılmalıdır. Proje değiştikçe geçerliliğini kaybeden birim testler yanıl-
tıcı negatif sonuçlar doğuracağından projenin gelişimi üzerinde negatif
bir etki yaratacaklardır.
5. Dış etkenlerden bağımsız olarak çalışabilmelidirler
Birim testler web servis, veri tabanı, gibi dış etkenlere bağımlı halde
yazılırlarsa taşınabilirliği azalacaktır. Bir programcının makinesinde
çalışan birim test bir başka programcının makinesinde çalışmayacak
veya yanlış sonuç verecektir.
66
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
Birim testleri yukarıdaki prensiplere uygun yazılmanın yanında bir test çer-
çevesi (test framework) içinde organize edilmelidir. Birim testleri manuel
olarak organize etmek hem zahmetli hem de hataya yol açabilecek bir yak-
laşım olacaktır. MSTest, XUnit, NUnit gibi çeşitli test çerçeve sistemleri
mevcuttur. Bu sistemler Visual Studio gibi yazılım geliştirme ortamlarına
entegre bir şekilde çalışabildiklerinden oldukça faydalıdırlar. Günümüzde
artık bir çerçeve sistem kullanmadan birim test yazmak söz konusu bile ol-
mamalıdır.
Daha çok teorik bir giriş formatında planladığımız bu makalemize az da ol-
sa Visual Studio’nun bir parçası olan MSTest çerçeve sistemini tanıtarak
devam edelim.
Visual Studio ortamında “Unit Test Project” proje taslağıyla yaratabileceği-
miz bu özel proje taslağı MSTest için gerekli referansların bizim için otoma-
tik olarak ekleyecektir. MSTest sınıf ve metotları bir takım özel test nitelikle-
ri ile dekore etme esasına dayalı bir sistemdir. Bu sistem test metotları içe-
risinden “doğrula” anlamına gelen Assert yardımcı sınıfı ve bu yardımcı
sınıfa ait bir takım statik metotlar ile test edilmekte olan birimlerin işlevleri-
nin doğru çalışıp çalışmadığını kontrol etmek prensibine dayalı çalışır. Kon-
figOku adlı sınıfımızı MSTest sistemine dayalı bir test projesi içinde test et-
mek için, projemize Unit Test Project taslağında yeni bir birim test projesi
ekleyelim ve aşağıdaki sınıfı yazalım.
KonfigOku Birim Testleri
[TestClass] public class KonfigOkuTestleri {
[TestMethod] public void KonfigOkuYapıcıMetotXmlMetniOkunamıyor() { try {//özellikle kötü xml ile dene KonfigOku konfigOku = new KonfigOku(""); } catch (Exception e) { Assert.IsTrue("Xml metni okunamıyor." == e.Message); } }
[TestMethod] public void KonfigOkuSunucuAlanındanBaşarılıOkumaYap() { KonfigOku konfigOku = new KonfigOku("<Konfig Sunu-cu=\"f5dergi.com\" Protokol=\"https\" Port=\"443\"></Konfig>"); Assert.IsTrue(konfigOku.Sunucu == "f5dergi.com"); }
[TestMethod] public void KonfigOkuSunucuAlanındanBaşarısızOkumaYap() { KonfigOku konfigOku = new KonfigOku("<Konfig Protokol=\"https\"
67
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
ReportViewer Kontrolü
Port=\"443\"></Konfig>"); try { string s = konfigOku.Sunucu; } catch (Exception e) { Assert.IsTrue("Sunucu okunamıyor." == e.Message); } } } [TestClass] ve [TestMeteod] adlı niteliklerle dekore ettiğimiz sınıf ve me-totlar bu niteliklerle dekore edildiklerinden dolayı Visual Studio tarafından kolaylıkla tespit edilebilirler. Bu da birim testlerin sağlaması gereken özellik-lerden ikincisi olan otomatik olarak çalışabilme özelliği konusunda büyük bir kolaylıktır. Hatta Visual Studio çok basit bir şekilde projemizi her derlediği-mizde bütün birim testleri çalıştıracak şekilde ayarlanabilir.
Böyle bir konfigürasyon ile yaptığımız değişiklik projenin her hangi bir yerin-
de bir hataya sebep olursa derleme yapar yapmaz negatif sonuç veren bi-
rim testleri fark edip gerekli kontrol ve tamiri yapabilecek duruma gelmiş
oluruz.
Birim testleri, birim testlerin neyi amaçladığını ve birim test çerçeve sistem-
lerinin faydalarını kısaca özetledik. Belirli bir külfet getirdiğini kabul etmekle
birlikte özellikle sistemlerin omurgası durumunda olan ve iş mantığı içeren
orta katmanlarda uygulanmasını önemle tavsiye ettiğimiz birim testler konu-
su yazılımcılar tarafından en azından iyi anlaşılması gereken bir konu oldu-
ğunu düşünüyoruz. Son yıllarda MVC teknolojilerinin popülerlik kazanması
test edilebilirliğinin klasik teknolojilere kıyasla çok daha fazla olmasından-
dır.
Yorum, istek ve düşüncelerinizi lütfen bizimle paylaşın.
http://f5dergi.com/Iletisim veya [email protected]
68
F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com
Yazılım profesyonelleri
tarafından,
yazılım profesyonelleri
için…
Ücretsiz abonelik: http://f5dergi.com