f5 dergisi

68
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 Ücretsizdir 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İ

Upload: fatos11

Post on 02-Aug-2015

120 views

Category:

Software


1 download

TRANSCRIPT

Page 1: 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İ

Page 2: F5 dergisi

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

Page 3: F5 dergisi

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

[email protected]

İÇİ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İ

Page 4: F5 dergisi

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

Page 5: F5 dergisi

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

Page 6: F5 dergisi

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

Page 7: F5 dergisi

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

Page 8: F5 dergisi

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

Page 9: F5 dergisi

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

Page 10: F5 dergisi

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

Page 11: F5 dergisi

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:

[email protected]

Page 12: F5 dergisi

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

Page 13: F5 dergisi

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

Page 14: F5 dergisi

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

Page 15: F5 dergisi

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

Page 16: F5 dergisi

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

Page 17: F5 dergisi

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

Page 18: F5 dergisi

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

Page 19: F5 dergisi

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)

Page 20: F5 dergisi

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

Page 21: F5 dergisi

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.

[email protected]

Page 22: F5 dergisi

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ı

Page 23: F5 dergisi

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)

Page 24: F5 dergisi

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ı

Page 25: F5 dergisi

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ı

Page 26: F5 dergisi

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ı

Page 27: F5 dergisi

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ı

Page 28: F5 dergisi

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ı

Page 29: F5 dergisi

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)

Page 30: F5 dergisi

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ı

Page 31: F5 dergisi

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ı

Page 32: F5 dergisi

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ı

Page 33: F5 dergisi

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ı

Page 34: F5 dergisi

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ı

Page 35: F5 dergisi

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ı

Page 36: F5 dergisi

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ı

Page 37: F5 dergisi

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ı

Page 38: F5 dergisi

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

Page 39: F5 dergisi

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

Page 40: F5 dergisi

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

Page 41: F5 dergisi

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

Page 42: F5 dergisi

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

Page 43: F5 dergisi

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

Page 44: F5 dergisi

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

Page 45: F5 dergisi

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=&quot;data source=|DataDirectory|\VT.sdf&quot;" 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

Page 46: F5 dergisi

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

Page 47: F5 dergisi

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

Page 48: F5 dergisi

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

Page 49: F5 dergisi

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

Page 50: F5 dergisi

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

Page 51: F5 dergisi

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

Page 52: F5 dergisi

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

Page 53: F5 dergisi

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

Page 54: F5 dergisi

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

Page 55: F5 dergisi

55

F5 DERGİ © KASIM-ARALIK 2012 http://f5dergi.com

http://f5dergi.com

Yazılım makaleleri,

örnek kaynak kodlar,

sektörden haberler...

Page 56: F5 dergisi

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

Page 57: F5 dergisi

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!

Page 58: F5 dergisi

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

Page 59: F5 dergisi

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

Page 60: F5 dergisi

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

Page 61: F5 dergisi

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

Page 62: F5 dergisi

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-

Page 63: F5 dergisi

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

Page 64: F5 dergisi

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

Page 65: F5 dergisi

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.

Page 66: F5 dergisi

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\"

Page 67: F5 dergisi

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]

Page 68: F5 dergisi

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