Makale Özeti

ASP.NET uygulamalarında Model View Controller(MVC) deseninin kolayca uygulanmasını sağlayan ASP.NET MVC Framework geçtiğimiz günlerde resmen duyuruldu. Bu yazımızda ASP.NET MVC Framework'ün genel yapısını göz atıp, Visual Studio 2008'de bir ASP.NET MVC projesini nasıl geliştirebileceğimizi inceleyeceğiz.

Makale

Model View Controller tasarım deseni(design pattern) bir uygulamanın model, view ve controller adında üç temel parça içerisinde geliştirilmesi esasına dayanmaktadır. Projenin bu şekilde üç parçaya ayrılmasının temel sebebi katmanlı mimari uygulamalarında da benimsediğimiz, proje bileşenlerinin birbirinden ayrıştırılabilecek katmanlarda, birbirlerinden bağımsız şekilde geliştirilebilmesini sağlamaktır(Seperation of Concerns). Bu esneklik projenin kolayca genişletilebilmesi, yazılan kodların kolayca güncellenebilmesini ve test işlemlerinin kolayca yapılabilmesini de beraberinde getirmektedir. Aslında burada bahsettiğimiz unsurlar büyük çaplı birçok projede ihtiyaç olan konulardır. Bu tarz ihtiyaçları gidermek için günümüzde çoğu uygulama platformunda farklı teknikler kullanarak, tasarım desenleri uygulanarak geliştirilen projenin çok sağlam temeller üzerine inşa edilmesi sağlanmaktadır. Web uygulamalarına baktığımız zaman özellikle son yıllarda MVC deseninin oldukça benimsendiği, başta Ruby ve Java gibi çoğu platformda kullanıldığı görülmektedir. Artan bu talep üzerine Microsoft'ta ASP.NET MVC Framework'ü geliştirerek artık web uygulamalarında ASP.NET Web Form'larından sonra ikinci bir uygulama geliştirme modeli sunmuştur. Aslında ASP.NET MVC'nin temelleri 2007 yılının ikinci yarısına dayanmaktadır. Microsoft yaklaşık 1.5 yıllık süreçte Preview, CTP ve Beta sürümleriyle çıkardığı bu ürünü yaklaşık 10 farklı sürümle geliştirmiş ve nihayi olarak Mart 2009 içerisinde resmi olarak piyasaya sürmüştür.

MVC desenin temel kullanım amaçlarından ve piyasadaki yerinden bahsettikten sonra dilerseniz ASP.NET uygulamalarına nasıl farklı bir tarz getirdiğine bakalım. ASP.NET MVC uygulamalarını vadettiği en önemli noktalar; test edilebilir, kolay genişletilebilir, Routing ile daha okunabilir URL adreslerine sahip, daha sade HTML çıktıya sahip uygulamalar tasarlanabilmesidir. Aslında bu konuyla ilgili olarak yazgelistir.com'da daha önceden yazılmış makalelerden faydalanabilirsiniz. Bu makaleler ASP.NET MVC'nin genel yapısını Preview sürümleri üzerinde anlatmaktadır. Preview sürümlerinden RTM sürümüne doğru baktığımızda modelin uygulanmasında ciddi bir değişiklik olmadığı için bu yazıları incelemeniz de fayda olacaktır:

- ASP.NET, MVC ile Kan Tazeliyor
- ASP.NET MVC Framework

Öncelikli olarak MVC Framework'ü ilk kez inceleyecek olanları belki de çok şaşırtacak birkaç durumdan bahsetmekte fayda var. Zira ASP.NET MVC web uygulamalarında çok farklı bir geliştirme tekniği kullanılmakta ve klasik ASP.NET uygulamalarının belki de en sevilen bazı özelliklerinden burada mahrum kalınmaktadır. ASP.NET MVC uygulamalarında PostBack ve Olay(Event) tabanlı geliştirme mekanizması yoktur, sunucu kontrolleri(GridView, Calendar, FileUpload... vb) kullanılamaz ve ViewState nesnesi yoktur! İlk kez duyanlar için oldukça şaşırtıcı olsa gerek. Akla gelen ilk soru belki de şu olacaktır; sunucu kontrolleri ve olay tabanlı geliştirme yoksa ben nasıl uygulama geliştireceğim, yoksa ASP.NET öncesinde kullanılan ASP'ye mi dönüş yapıyoruz? Tabi ki hayır, ama kodlama mantığına baktığımız zaman büyük benzerliklerin olacağını da göreceğiz. Yine MVC Framework'ün gelişim sürecinde ASP.NET ekibi Visual Studio'ya eklediği birçok araç sayesinde kod yazımını oldukça azaltarak WebForm'lardakine benzer kolaylıkları programcılara sağlamakta. Burada vurgulanması gereken en önemli noktalardan birisi de şu olacaktır; ASP.NET MVC uygulamalarında yukarıda bahsettiğimiz özellikleri kullanamıyoruz, ancak uygulama tamamen ASP.NET Runtime'ı üzerinde çalışıyor. Yani siz uygulamayı geliştirirken arka planda .NET Framework'ün geriye kalan tüm nimetlerini kullanabiliyorsunuz. İşin .NET tarafında bu durumlar söz konusu. İyi ama nedir bu Model View Controller, herbir parçanın görevi nedir diye soracak olursanız, gelin bu kısmı ayrı bir paragrafta ele alalım. Öncelikle bu üç bileşene kısaca göz atalım:

- Controller: Uygulamaya gelen talepleri yöneten katmandır. Controller nesneleri içerisinde action adı verilen metotlar bulunur ve bu metotların görevleri gelen talepleri uygun View ve Model nesneleri üzerinden değerlendirmektir. Gelen sayfa talepleri URL bazlı olarak çözümleyerek ilgili action metoduna iletir. Controller içerisinde yer alan action metotları projenin View ve Model bileşenleriyle doğrudan iletişime geçebilir.
- Model: Uygulamanın veriye erişim ve iş mantığını gerçekleştiren nesnelerini saklayan kısmıdır. LINQ to SQL(.dbml) ile Entity Framework(.edmx) dosyaları veya DataTable, DataSet, DataReader gibi nesnelerle veri taşıyan tiplerimiz bu katmanda yer almaktadır. Veriye erişim gerektiğinde Controller ve View bu kısımda yer alan nesnelerle iletişime geçebilir.
- View: Projenin UI dosyaları burada yer almaktadır. Master sayfalar, .aspx sayfalar, user control'ler, yani kullanıcının göreceği dosyalar projenin bu kısmında yer alacaktır.

İstemciden gelen talepler Controller içerisinde yer alan nesneler tarafından yakalanır. İlgili Controller gelen URL'i çözümleyerek kendi içerisindeki uygun action metodunu çağırır. Eğer ilgili action'da veri ile ilgili işlemler yapılacaksa Model kısmındaki nesnelere erişilir, getirilen veriler ilgili View nesnesine gönderilir. Eğer veri ile ilgili işlemler yoksa talep doğrudan yine View nesnesine gönderilebilir. View ise sayfa içeriğini render ederek HTML çıktıyı oluşturur ve çıktı istemciye gönderilir. Aslında genel işleyişe baktığımızda standart ASP.NET uygulamalarından çok farklı olduğu dikkatimizi çekiyor. MVC uygulamalarında talep doğrudan .aspx sayfalarına iletilmez, Controller içerisinde yer alan action metotları ilgili sayfaya gidilmesini ve üretilen HTML çıktının istemciye iletilmesini sağlar. Dolayısıyla burada istemci doğrudan sayfalarla iletişime geçmez, ancak yazılarn farklı URL'ler ile sayfalara gidebilir. Aşağıdaki şekilde istemciden gelen bir talebin sunucu tarafında nasıl ele alınacağı görülmektedir.


Resim: İstemciden gelen bir sayfa talebinin sunucuda işleniş yolu

ASP.NET MVC uygulamaları URL'in uygun formatta çözümlenmesiyle çalışmaktadır. Burada URL Routing adını verdiğimiz bir kavramla karşılaşıyoruz aslında. ASP.NET MVC'de standart olarak controller/action/id şeklindeki dizilimle URL'lere gelen talepler değerlendirilir. Bunun dışında farklı URL formatları da belirlenebilir, kendi özel URL formatlarınızı belirleyebilir, hatta URL içerisine yazılacak değerlere kısıtlamalar(constraints) dahi getirebilirsiniz. (URL Routing ile ilgili eğer fırsatım olursa ayrıca detaylı bir makale yazmayı planlıyorum) Products/Edit/3 şeklindeki bir URL'de Products controller'ı, Edit action'ı ve 3 ise id'yi simgeler. Aşağıda bazı URL formatları ve taleplerin izleyecekleri yollar listelenmiştir.

URL İzlenecek yol
site.com/Products/ ProductsController içerisinde yer alan Index action metodu çağrılır. Eğer adreste bir action belirtilmemişse varsayılan action Index olur.
site.com/Products/List ProductsController içerisinde yer alan List action metodu çağrılır.
site.com/Products/Edit/3 ProductsController içerisinde yer alan Edit action metodu çağrılır. Bu metoda 3 değeri id parametresi olarak gönderilir.
site.com/Categories/Details/4 CategoriesController içerisinde yer alan Details action metodu çağrılır. Bu metoda 4 değeri parametre olarak gönderilir.
site.com/Categories/Edit/4 CategoriesController içerisinde yer alan Edit action metodu çağrılır. Bu metoda 4 değeri parametre olarak gönderilir.

Aslında buraya kadar ASP.NET MVC'nin genel yapısından ve WebForm'lar üzerinde uygulama geliştiren programcılara yabancı gelebilecek özelliklerinden bahsettik. Örnek bir uygulama ile bu yapıyı biraz daha tanımaya çalışalım. Öncelikli olarak MVC Framework için gerekli bir araç kurulumu yapmamız lazım. http://www.microsoft.com/downloads/details.aspx?FamilyID=53289097-73ce-43bf-b6a6-35e00103cb4b&displaylang=en adresinden gerekli dosyayı bilgisayarınıza yükleyebilirsiniz. Kurulum için .NET Framework 3.5 SP1 ve Visual Studio 2008 SP1'in bilgisayarınızda kurulu olması gerekir. ASP.NET versiyonlamasıyla ilgili olarakta önemli bir noktayı belirtmekte fayda var; ASP.NET MVC Framework ASP.NET 4.0 ile mimariyle bütünleşik hale gelecek.

Kurulumu yaptıktan sonra Visual Studio 2008'de File > New Project linkini, açılan pencereden kullanacağımız dilin altında yer alan proje türlerinden Web'i seçiyor ve buradan da ASP.NET MVC Web Application proje şablonunu seçiyoruz.


Resim: ASP.NET MVC projesinin oluşturulması

Proje oluşturulurken Visual Studio, MVC uygulaması ile birlikte bir de test projesi oluşturmak için onay penceresi çıkaracaktır. Bu test projesi, MVC uygulamamızdaki action metotlarını test edebilmemiz, yani unit test'ler yapabilmemiz için kullanılabilir. Eğer test işlemleri de yapacaksanız karşınıza çıkan bu pencereden Yes... ile başlayan seçeneği kullanabilirsiniz. Burada MVC Framework ile gelen standart Visual Studio Unit Test framework'ü kullanılabileceği gibi üçüncü parti test framework'leri de kullanılabilir. Biz şimdilik test projesi kullanmayacağımız için No... ile başlayan seçeneği seçiyor ve OK butonuna tıklıyoruz.


Resim: Unit Test Projesi seçenekleri

ASP.NET MVC Web uygulamamız oluşturuldu. İlk olarak Solution Explorer'a bakıyor ve bize biraz yabancı gelebilecek dizin yapısını inceliyoruz. Aşağıdaki resimde projemizin Solution Explorer'daki görüntüsü bulunmaktadır.


Resim: ASP.NET MVC projemizin dosya ve klasör yapısı

Resim üzerinde klasörlerle ilgili kısa açıklamaları bulabilirsiniz. Bizim için burada önemli olan dizinler Controllers, Models ve Views olacaktır. Herbirinin adından da anlaşılacağı üzere Model View Controller deseninin dosya olarak uygulanışı bu dizinler içerisinde olacaktır. Bunun dışında Content css, resim gibi dosyaları, Scripts ise JavaScript kod kütüphanalerini içermesi için özel ayrılmış dizinlerdir. Scripts klasöründe AJAX, MVC için AJAX ve jQuery kütüphanelerini otomatik olarak geldiği de görülmektedir. Controllers klasöründe AccountController ve HomeController adında iki Controller class'ı bulunmaktadır. Bu iki class URL içerisinde controller değeri Home ve Account olan talepleri karşılayacaktır. View klasöründe ise Account, Home ve Shared klasörleri yer almaktadır. Shared tüm View nesnelerinde ortak kullanılabilecek master sayfa, user control gibi dosyalar içindir. Diğer iki klasöre baktığımızda ise isim olarak Controller sınıflarımızla benzerlik göstermektedir. HomeController'a karşılık gelen Home, AccountController'a karşılık gelen Account adında bir dizin ve bu dizinler altında da kullanıcıların görebileceği aspx dosyaları yer alır. Aslında buradaki isimlendirmeler tesadüf olarak Controller sınıflarına benzemiyor, MVC uygulamasındaki isimlendirme standartına göre HomeController gibi bir controller sınıfına gelen talepler Views altında Home dizini altında yer alan dosyalara iletilecektir. Eğer kendi özel iş mantığımıza göre nesnelerinizi oluşturmak istersek; örneğin veritabanındaki ürünlerimizle ilgili sayfalar oluşturmak için Controllers dizinine UrunController adında bir controller sınıfı ve Views dizini altında da Urun adında bir dizin oluşturmanız gerekecektir.

ASP.NET MVC uygulamalarında URL'deki dizime göre taleplerin işlendiğinden bahsetmiştik. Global.asax dosyası içerisinde uygulamanın başlangıcında(Application_Start metodunda) URL formatlamasının nasıl yapılacağı belirlenmektedir. System.Web.Routing isimalanı altında yer alan RouteTable sınıfının Routes koleksiyonu uygulama genelinde geçerli olacak URL Routing ayarlarını saklamaktadır. Bu koleksiyona eklenecek her URL formatı MVC projesine gelen URL taleplerinde değerlendirilecektir. Aşağıda ASP.NET MVC uygulamalarının varsayılan URL Routing ayarı görülmektedir.

Global.asax
publicue;">class MvcApplication : System.Web.HttpApplication

{

    public static void RegisterRoutes(RouteCollection routes)

    {

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

        routes.MapRoute(

            "Default",                                              // Route name

            "{controller}/{action}/{id}",                           // URL with parameters

            new { controller = "Home", action = "Index", id = "" // Parameter defaults

        );

    }

 

    protected void Application_Start()

    {

        RegisterRoutes(RouteTable.Routes);

    }

}

 

Routes koleksiyonunun MapRoute metoduna bakacak olursak; ilk parametrenin route ismini, ikinci parametrenin URL formatını, üçüncü parametrenin ise bu Route ayarının varsayılan değerlerini belirlediğini görürüz. Bu tanımlama sayesinde uygulamaya;

site.com/Products/Edit/3
şeklinde bir URL talebi gelirse
 - controller: ProductsController (Controllers klasörü altındaki sınıf)
 - action: Edit (ProductsController sınıfı içerisindeki Edit metodu)
 - id: 3 (Edit metoduna gönderilecek parametre)

olacaktır. Eğer site.comşeklinde bir talep gelseydi controller HomeControler(URL'de controller değeri belirtilmediği için HomeController'a gidilir), action ise Index olacaktı. Çünkü bu değerlerin boş bırakılması durumunda varsayılan değerlerinin ne olacağı yukarıdaki MapRoute metodunun son parametresinde bu şekilde belirtilmişti. Özel bir not; MapRoute metodundaki son parametre C# 3.0 ile gelen anonim tip(anonymous type) özelliği kullanılarak yazılmıştır.Controller kendisine gelen talebi View'a iletir demiştik. Eğer talep esnasında veriye erişmek gerekiyorsa action metodu içerisinden(yani Controller katmanından) Model bileşenlerine erişilir. Controller Model katmanından çektiği veriyi View'a iletir ve View'da HTML çıktıyı oluşturarak istemciye iletir. Bu arada View içerisinden de doğrudan Model nesnelerine erişilebileceğini, ancak Model içerisinden View'a erişilemeyeceğini de belirtmekte fayda var.

Açtığımız projeyi çalıştırarak MVC'nin temel anlamda işleyişini incelemeye çalışalım. Bu işlemin öncesinde HomeController sınıfındaki About metodunun ilk satırına bir break-point ekleyerek debug modda bu sayfaya yapılan bir talebin bu nasıl işleneceğini izleyelim. F5 tuşuna basarak projenin ana sayfasına gidiyor ve sağ üstteki About linkine tıklıyoruz.


Resim: Home/About şeklindeki URL'e gelen talebin işleyişi

Görüleceği gibi About sayfasında, yani Home/About şeklinde gelen URL talebinde HomeController sınıfının About adındaki action metodu çağrıldı ve bu metottan Views klasörünün altındaki Home/About.aspx adındaki View nesnesi görüntülendi. Anlaşıldığı gibi HomeController nesnesine gelen bir talepte Views dizinindeki Home dizini altında yer alan aspx dosyaları kullanıcıya gönderiliyor. Dosya adının belirlenmesinde de action metodunun isminin etkili olduğu görünmekte. ASP.NET MVC uygulamalarında View nesnelerinin isimlendirme standartı ve Controller nesnesi ile arasındaki ilişki bu şekildedir.

Bu örnekte dikkat çeken bir diğer noktada URL'nin yazılış şeklidir. Bildiğiniz gibi klasik ASP.NET uygulamalarında bu URL aslında bir klasörü simgeler. Klasik ASP.NET uygulamalarında sayfalara yapılan her talebin sonunda sayfanın uzantısı, yani .aspx olmalıdır. Ancak burada adresin sonunda herhangi bir uzantı olmadığı dikkat çekmektedir. Hatta projemizin dizin yapısına baktığımızda root dizinimizde Home adında bir klasör olmadığını da görürüz. MVC uygulamaları URL Routing ile çalıştığı için bu şekilde gelen URL'ler arka planda uygun View nesnesi üzerinden cevaba dönüştürülür. Bu şekilde yazılan URL'ler hem kullanıcılar için daha okunaklı olacak hem de arama motorlarının sayfamızı daha iyi analiz etmesini sağlayacaktır.

Dilerseniz projemize bir de LINQ to SQL sınıfı; yani bir Model nesnesi ekleyerek Controller'ın veriyi nasıl View'a taşıdığını inceleyelim. Models klasörünün üzerine sağ tıklayarak Add > New Item diyoruz ve LINQ to SQL Classes'ı seçerek adı Northwind.dbml olan yeni bir dosya ekliyoruz. Northwind veritabanındaki Products tablosunu entity olarak bu dosya içerisinde ekliyoruz. Artık projemizde Product adında bir Model nesnemiz var. Sıra geldi gerekli Controller ve View nesnelerini oluşturmaya. Controllers klasörüne sağ tıklayarak Add > Controller seçeneğine tıklıyoruz. Karşımızda çıkan pencereden Controller nesnemize UrunController adını veriyoruz. Dikkat edeceğiniz üzere bu penceredeki seçim kutusu ile otomatik olarak belirli metotların otomatik tanımlanmasını sağlayabiliriz.


Resim: UrunController dosyasının projemize eklenmesi

UrunController sınıfı System.Web.Mvc isimalanı altında yer alan Controller adındaki bir sınıftan kalıtılmaktadır. İçerisindeki Index adındaki metodu ben Detay olarak değiştiriyorum ve metoda id adında Int32 tipinden bir de parametre ekliyorum. Gelelim Model nesnesine nasıl erişeceğimize; aslında bildiğimiz klasik kodlama işleminden başka birşey yapmayacağız. Detay metodu içerisinde bir context nesnesi oluşturarak gerekli LINQ sorgusu ile id bilgisi verilen ürünün bilgilerini getireceğiz. Metodumuzun son hali aşağıdaki gibidir.

Controllers/UrunController.cs
...
using
AspnetMvcGiris.Models;

 

namespace AspnetMvcGiris.Controllers

{

    public class UrunController : Controller

    {

        public ActionResult Detay(int id)

        {

            NorthwindDataContext ctx = new NorthwindDataContext();

            Product urun = (from u in ctx.Products

                         where u.ProductID == id

                         select u).FirstOrDefault();

            return View(urun);

        }

    }

}

LINQ sorgusu sonucunda getirilen ürün bilgisi return View(urun) ile View sayfasına gönderilmektedir. Buradaki View metodu, şu an içerisinde bulunduğumuz action'ın View dosyasını çağıracaktır. Bu dosyaya da Model adındaki bir nesne içerisinde ürünün bilgilerini taşıyacaktır. MVC uygulamalarındaki sıkıntılarımızdan birisi ASP.NET sunucu kontrollerini kullanamıyor olmamızdı. Bu ilk aşamada View içerisindeki sayfalarımızı tasarlamakta bize zorluk çıkaracak gibi görünse de Visual Studio üzerindeki ASP.NET MVC için gelen özel menüler ve araçlar sayesinde yine daha az kod yazarak arayüzler oluşturabiliyoruz. Yukarıda gördüğümüz Detay adındaki metodun içerisinde herhangi bir yere sağ tıklayarak açılan menüden Add View seçeneğine tıklayalım. Açılan pencere Controller nesnemizden ve action metodumuzdan yola çıkarak bize bir View nesnesi oluşturacaktır. Pencereden eğer Create a strongly-typed view seçeneğini seçersek, aktifleşen kısımlarda oluşan View nesnesinde hangi Model tipine ait bilgilerin görüntüleneceğini(View data class) ve sayfa içeriğinin(View content); yani sayfa detay bilgisi mi gösterecek yoksa güncelleme işlemi mi yapacak, bu bilgileri belirleyebiliyoruz. Add butonuna tıkladığımızda ise Visual Studio'nun ihtiyacımız olan View nesnesini otomatik olarak oluşturacağını göreceğiz. (Views klasörü altına Urun, bu klasörün altına da Detay.aspx adında bir dosya eklenir)


Resim: Hazırladığımız action metodu üzerinden gerekli olan View nesnesini kolay şekilde oluşturabiliriz

Detay.aspx sayfasını incelediğimizde Product nesnesine göre özel bir arayüz hazırlandığını ve ürünün bilgilerinin de Model adındaki nesne üzerinden taşındığını görebiliriz. Uygulamayı çalıştırıp Urun/Detay/3 şeklindeki bir URL'yi talep edersek aşağıdaki gibi bir çıktı alırız.


Resim: Urun/Detay/3 şeklindeki bir URL talebinde karşılaştığımız sayfa

URL'deki id değerine göre ürün bilgilerini sayfamızda görebildik. Eğer sayfanın görünümünü değiştirmek istersek Views/Urun/Detay.aspx sayfası içerisinde gerekli değişiklikleri yapabiliriz.

Bu makalemizde ASP.NET MVC Framework'ü genel hatlarıyla incelemeye, ASP.NET uygulamalarına nasıl farklılıklar getirdiğine ve basit olarak bir web uygulamasının parçalarını bu framework'ü kullanarak nasıl geliştirebileceğimize bakmaya çalıştık. İlerleyen zamanlarda yine ASP.NET MVC ile ilgili farklı konulardaki yazıları da sizlerle paylaşabilmek dileğiyle...



Uğur UMUTLUOĞLU
www.umutluoglu.com
twitter.com/umutluoglu