9. Vaka çalışması
9.1. Introduction
URL [http://tahe.developpez.com/dotnet/pam-aspnet/] adresinde bulunan bir makalede daha önce yayınlanmış bir vaka çalışmasını sunacağız. Bu makalede, vaka çalışması klasik ASP.NET ve ORM NHibernate kullanılarak gerçekleştirilmiştir. Burada ise ASP.NET, MVC ve ORM Entity Framework modelleriyle bunu gerçekleştireceğiz. Mevcut makalede olduğu gibi, vaka çalışması bir üniversite dersi olarak sunulmaktadır. Dolayısıyla bu çalışma öğrencilere yöneliktir. Tüm sorular için, faydalı kaynakları belirtmek amacıyla az önce ayrıntılı olarak ele aldığımız bölümlere atıfta bulunulmaktadır.
9.2. Çözülmesi gereken sorun
Bir kullanıcının, bir belediyenin “Maison de la petite enfance” derneğine bağlı çocuk bakıcılarının maaş hesaplamalarını simüle etmesine olanak tanıyan bir web uygulaması yazmak istiyoruz. Uygulamanın DotNet kodunun organizasyonuna da kodun kendisine olduğu kadar önem vereceğiz.
Uygulama, APU ve [Application à Page Unique] türünde olacak ve sunucuyla iletişim kurmak için yalnızca Ajax çağrıları kullanacaktır. Kullanıcıya aşağıdaki görünümleri sunacaktır:
- simülasyon formunu gösteren [VueSaisies] görünümü

- Simülasyonun ayrıntılı sonucunu görüntülemek için kullanılan [VueSimulation] görünümü:

- müşteri tarafından yapılan simülasyonların listesini veren [VueSimulations] görünümü

- Müşterinin simülasyonu olmadığını veya artık simülasyonu kalmadığını gösteren [VueSimulationsVides] görünümü:

- bir veya daha fazla hata olduğunu gösteren [VueErreurs] görünümü (burada SGBD ve MySQL durdurulmuştur):

9.3. Uygulamanın mimarisi
Uygulamanın mimarisi şu şekilde olacaktır:
![]() |
[EF5] katmanı, ORM Entity Framework 5'i ifade eder. Kullanılacak SGBD, MySQL olacaktır.
Bu uygulamayı önce simüle edilmiş bir [métier] katmanı ile oluşturacağız:
![]() |
Bu sayede yalnızca [web] katmanına odaklanabileceğiz. Simüle edilmiş [métier] katmanı, gerçek [métier] katmanının arayüzüne uyacaktır. [web] katmanı faaliyete geçtiğinde, [métier], [DAO] ve [EF5] katmanları oluşturulacaktır.
9.4. Veritabanı
Maaş bordrosunu oluşturmak için gerekli statik veriler, MySQL adlı ve [dbpam_ef5] (pam=Çocuk Bakıcısı Maaşı) olarak adlandırılan bir veritabanında yer alır. Bu veritabanının root adlı, şifresiz bir yöneticisi vardır. Üç tablo içerir:

EMPLOYES (INDEMNITE_ID) sütunu ile INDEMNITES (ID) sütunu arasında bir yabancı anahtar ilişkisi bulunmaktadır. Bu veritabanının yapısı, EF5 ile birlikte kullanılması nedeniyle belirlenmiştir. Uygulamanın alt katmanlarını oluştururken bu konuya tekrar değineceğiz.
Yapı:
![]() |
|
İçeriği şu şekilde olabilir:
![]()
Yapı:
![]() |
|
İçeriği şu şekilde olabilir:
![]()
Sosyal güvenlik prim oranları çalışandan bağımsızdır. Yukarıdaki tablo sadece bir satırdan oluşur.
![]() |
|
İçeriği şu şekilde olabilir:
![]()
9.5. 'de bir çocuk bakıcısının maaşının hesaplanma yöntemi
Şimdi, bir çocuk bakıcısının aylık maaşının hesaplanma yöntemini sunuyoruz. Örnek olarak, ödeme yapılacak ay boyunca 20 gün boyunca 150 saat çalışmış olan Bayan Marie Jouveinal'ın maaşını ele alıyoruz.
Aşağıdaki unsurlar dikkate alınır: | [TOTALHEURES]: ay içinde çalışılan toplam saat sayısı [TOTALJOURS]: Ay içinde çalışılan toplam gün sayısı | [TOTALHEURES]=150 [TOTALJOURS]= 20 |
Çocuk bakıcısının temel maaşı aşağıdaki formülle hesaplanır: | [SALAIREBASE]=([TOTALHEURES]*[BASEHEURE])*(1+[INDEMNITESCP]/100) | [SALAIREBASE]=(150*[2.1])*(1+0,15)= 362,25 |
Bu temel maaştan bir dizi sosyal güvenlik primi kesilmelidir: | Genel sosyal katkı payı ve sosyal borç geri ödemesi katkı payı: [SALAIREBASE]*[CSGRDS/100] İndirilebilir genel sosyal katkı payı: [SALAIREBASE]*[CSGD/100] Sosyal güvenlik, dul ve yaşlılık sigortası: [SALAIREBASE]*[SECU/100] Ek Emeklilik + AGPF + İşsizlik Sigortası: [SALAIREBASE]*[RETRAITE/100] | CSGRDS: 12,64 CSGD: 22,28 Sosyal Güvenlik: 34,02 Emeklilik: 28,55 |
Toplam sosyal güvenlik primleri: | [COTISATIONSSOCIALES] = [SALAIREBASE] *(CSGRDS+CSGD+SECU+RETRAITE)/100 | [COTISATIONSSOCIALES]=97,48 |
Ayrıca, çocuk bakıcısı, çalıştığı her gün için geçim ödeneği ve yemek ödeneği almaya hak kazanır. Bu kapsamda aşağıdaki ödenekleri alır: | [Indemnités]=[TOTALJOURS]*(ENTRETIENJOUR+REPASJOUR) | [INDEMNITES]=104 |
Sonuç olarak, çocuk bakıcısına ödenecek net maaş şu şekildedir: | [SALAIREBASE] - [COTISATIONSSOCIALES] + [INDEMNITÉS] | [salaire NET]=368,77 |
9.6. [web] katmanının Visual Studio projesi
Uygulamanın Visual Web Developer projesi şu şekilde olacaktır:
![]() |
- [1]'te, [pam-web-01] projesinin genel yapısı;
- [2]'te, [Content] klasörü, projenin statik kaynaklarının yerleştirildiği klasördür:
- [indicator.gif]: Ajax isteğinin tamamlanmasını beklerken gösterilen animasyonlu resim,
- [standard.jpg]: çeşitli görünümlerin arka plan görüntüsü,
- [Site.css]: uygulamanın stil sayfası;
- [3] dosyasında, [PamController] uygulamasının tek denetleyicisi;
- [4]'te, uygulama için gerekli olan ancak MVC'in bileşenleri olarak sınıflandırılamayan sınıflar:
- [ApplicationModelBinder]: [Application] kapsamındaki verilerin eylem modeline dahil edilmesini sağlayan sınıf,
- [SessionModelBinder]: [Session] kapsamındaki verileri eylem modeline dahil etmeyi sağlayan sınıf,
- [Static]: statik yöntemler içeren bir yardımcı sınıf;
- [5]'te, ister eylem ister görünüm şablonları olsun, uygulamanın şablonları:
- [ApplicationModel]: [Application] kapsamındaki verileri içeren şablon,
- [SessionModel]: [Session] kapsamındaki verileri içeren model,
- [Simulation]: maaş hesaplama simülasyonunun öğelerini kapsayan sınıf,
- [IndexModel]: uygulama tarafından görüntülenen ilk görünüm [Index]'in modeli;
- [6]'te, uygulamanın yerelleştirilmesi için gerekli olan JS komut dosyaları;
- [7] dosyasında, uygulamanın uluslararasılaştırılması, istemci tarafında doğrulama ve AJAX uyumu için gerekli olan JQuery ailesine ait JS komut dosyaları;
- [8] içinde, [myScripts.js], kendi JS komut dosyalarımızı içeren dosyadır;
- [9] dosyasında ise uygulamanın görünümleri yer almaktadır:
- [Index]: ana sayfa,
- [Formulaire]: Çalışan bilgilerinin, çalışma saatlerinin ve günlerinin girildiği form,
- [Simulation]: simülasyonu gösteren görünüm,
- [Simulations]: Yapılan simülasyonların listesini gösteren görünüm,
- [Erreurs]: Olası hataların listesini gösteren görünüm,
- [InitFailed]: Uygulamanın başlatılması başarısız olursa hata mesajlarını gösteren görünüm;
- [10]'te, [_Layout] uygulamasının ana sayfası;
- [11]'te, uygulamayı yapılandırmak için kullanılan [Web.config] ve [Global.asax] dosyaları.
9.7. Adım 1 – Simüle edilmiş [métier] katmanının kurulması
Bundan itibaren, vaka çalışmasını gerçekleştirmek için izlenmesi gereken adımları açıklayacağız. Gerekli olduğunda, istenen çalışmayı gerçekleştirmek için tekrar okunması gereken bölüm numarasını belirteceğiz. Projenin bazı öğeleri, bu belgenin bulunduğu sitede yer alan [aspnetmvc-support.zip] klasöründe size verilmiştir. Bu klasörde, aşağıdaki içeriğe sahip [étudedecas-support] klasörü bulunmaktadır:
![]() |
Proje ayrıca önceki bölümlerde sunulan öğeleri de içermektedir. Bu öğeleri, PDF dosyası ile Visual Studio arasında kopyala/yapıştır yaparak aktarmanız yeterlidir.
9.7.1. Uygulamanın tam Visual Studio çözümü
Öncelikle, içinde iki proje oluşturacağımız bir Visual Studio çözümü oluşturacağız:
- simüle edilmiş katman için bir proje [métier];
- MVC web katmanı için bir proje.
![]() |
İki araç kullanacağız:
- [métier] katmanını oluşturmak için Visual Studio Express 2012 for Desktop;
- [web] katmanını oluşturmak için kullanılacak Visual Studio Express 2012 for Web.
Visual Studio Express for Desktop ile bir [pam-td] çözümü oluşturuyoruz:
![]() |
- [1]'te bir C# uygulaması seçin;
- [2]'te, [Application console]'i seçin;
- [3]'te, çözüme bir ad verin;
- [4]'te, bu çözüm için bir dizin oluşturun;
- [5]'te, [métier] katmanına bir ad verin;
- [6]'te, oluşturulan çözüm.
9.7.2. [métier] katmanının arayüzü
Katmanlı bir mimaride, katmanlar arası iletişimin arayüzler aracılığıyla gerçekleştirilmesi iyi bir uygulamadır:
![]() |
[métier] katmanı, [web] katmanına hangi arayüzü sunmalıdır? Bu iki katman arasında hangi etkileşimler mümkündür? Kullanıcıya sunulacak web arayüzünü hatırlayalım:
![]() |
- Formun ilk görüntülendiği anda, [1]'te çalışanların listesi bulunmalıdır. Basit bir liste yeterlidir (Soyadı, Adı, SS). Seçilen çalışanla ilgili ek bilgilere (6 ila 11 numaralı bilgiler) erişmek için SS numarası gereklidir.
- 12 ila 15 numaralı bilgiler, çeşitli prim oranlarını gösterir.
- 16 ila 19 numaralı bilgiler, çalışanın ek ödemeleridir.
- 20 ila 24 numaralı bilgiler, kullanıcı tarafından girilen 1 ila 3 numaralı veriler temel alınarak hesaplanan maaş bileşenleridir.
[métier] katmanı tarafından [web] katmanına sunulan [IPamMetier] arayüzü, yukarıdaki gereksinimleri karşılamalıdır. Birçok farklı arayüz seçeneği mevcuttur. Biz şu arayüzü öneriyoruz:
using Pam.Metier.Entites;
namespace Pam.Metier.Service
{
public interface IPamMetier
{
// tüm çalışan kimliklerinin listesi
Employe[] GetAllIdentitesEmployes();
// ------- maaş hesaplaması
FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
}
- 7. satır: [1] açılır menüsünün doldurulmasını sağlayacak yöntem
- 10. satır: 6 ila 24 numaralı bilgileri elde etmeyi sağlayacak yöntem. Bu bilgiler, yakında açıklayacağımız [FeuilleSalaire] türünde bir nesne içinde toplanmıştır.
Bu arayüzü [metier/service] adlı bir klasöre yerleştireceğiz:
![]() |
9.7.3. [métier] katmanındaki varlıklar
Önceki arayüz, tanımlamamız gereken [Employe] ve [FeuilleSalaire] adlı iki sınıfı kullanır:
- [Employe], veritabanındaki [employes] tablosunun bir satırının görüntüsüdür;
- [FeuilleSalaire], bir çalışanın maaş bordrosudur.
Varlıklar, projenin [metier / entites] klasörüne yerleştirilecektir:
![]() |
Nihai mimaride, [métier] katmanı veritabanındaki resim varlıklarını işleyecektir:

Veritabanındaki üç tablonun satırlarını temsil etmek için aşağıdaki sınıfları kullanacağız. Farklı alanların anlamlarını öğrenmek için 9.4 numaralı paragrafa bakınız.
[Employe] Sınıfı
Bu sınıf, [employes] tablosundaki bir satırı temsil eder. Kodu şöyledir:
using System;
namespace Pam.Metier.Entites
{
public class Employe
{
public string SS { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Adresse { get; set; }
public string Ville { get; set; }
public string CodePostal { get; set; }
public Indemnites Indemnites { get; set; }
// imza
public override string ToString()
{
return string.Format("Employé[{0},{1},{2},{3},{4},{5}]", SS, Nom, Prenom, Adresse, Ville, CodePostal);
}
}
}
[Indemnites] Sınıfı
Bu, [indemnites] tablosundaki bir satırı temsil eder. Kodu şöyledir:
using System;
namespace Pam.Metier.Entites
{
public class Indemnites
{
public int Indice { get; set; }
public double BaseHeure { get; set; }
public double EntretienJour { get; set; }
public double RepasJour { get; set; }
public double IndemnitesCp { get; set; }
// imza
public override string ToString()
{
return string.Format("Indemnités[{0},{1},{2},{3},{4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCp);
}
}
}
[Cotisations] sınıfı
Bu, [cotisations] tablosundaki bir satırı temsil eder. Kodu şöyledir:
using System;
namespace Pam.Metier.Entites
{
public class Cotisations
{
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
// imza
public override string ToString()
{
return string.Format("Cotisations[{0},{1},{2},{3}]", CsgRds, Csgd, Secu, Retraite);
}
}
}
Sınıfların, tablolardaki [ID] ve [VERSIONING] sütunlarını içermediğine dikkat edilmelidir. Bu sütunlar, ORM ve EF5 kullanıldığında yararlı olmakla birlikte, simüle edilen [métier] katmanı bağlamında gerekli değildir.
[FeuilleSalaire] sınıfı, daha önce sunulan formdaki 6 ila 24 numaralı bilgileri kapsar:
namespace Pam.Metier.Entites
{
public class FeuilleSalaire
{
// otomatik özellikler
public Employe Employe { get; set; }
public Cotisations Cotisations { get; set; }
public ElementsSalaire ElementsSalaire { get; set; }
// ToString
public override string ToString()
{
return string.Format("[{0},{1},{2}]", Employe, Cotisations, ElementsSalaire);
}
}
}
- 7. satır: maaşı hesaplanan çalışana ait 6 ila 11 numaralı bilgiler ve ek ödemelerine ait 16 ila 19 numaralı bilgiler. Burada, bir [Employe] nesnesinin, ek ödemelerini temsil eden bir [Indemnites] nesnesini kapsadığını unutmamak gerekir;
- 8. satır: 12 ila 15 numaralı bilgiler;
- 9. satır: 20 ila 24 numaralı bilgiler;
- 12-14. satırlar: [ToString] yöntemi.
[ElementsSalaire] sınıfı, formdaki 20 ila 24 numaralı bilgileri kapsar:
namespace Pam.Metier.Entites
{
public class ElementsSalaire
{
// otomatik özellikler
public double SalaireBase { get; set; }
public double CotisationsSociales { get; set; }
public double IndemnitesEntretien { get; set; }
public double IndemnitesRepas { get; set; }
public double SalaireNet { get; set; }
// ToString
public override string ToString()
{
return string.Format("[{0} : {1} : {2} : {3} : {4} ]", SalaireBase, CotisationsSociales, IndemnitesEntretien, IndemnitesRepas, SalaireNet);
}
}
}
- 6-10. satırlar: daha önce açıklanan iş kurallarında belirtildiği gibi maaş bileşenleri;
- 6. satır: Çalışanın, çalışılan saat sayısına bağlı olarak hesaplanan temel maaşı;
- 7. satır: bu temel maaştan kesilen primler;
- 8. ve 9. satırlar: çalışanın endeksine ve çalışılan gün sayısına bağlı olarak temel maaşa eklenecek ek ödemeler;
- satır 10: ödenecek net maaş;
- satır 14-17: sınıfın [ToString] yöntemi.
9.7.4. [PamException] sınıfı
Uygulamamız için özel bir istisna türü oluşturuyoruz. Bu, aşağıdaki [PamException] türüdür:
using System;
namespace Pam.Metier.Entites
{
// istisna sınıfı
public class PamException : Exception
{
// hata kodu
public int Code { get; set; }
// yapıcılar
public PamException()
{
}
public PamException(int Code)
: base()
{
this.Code = Code;
}
public PamException(string message, int Code)
: base(message)
{
this.Code = Code;
}
public PamException(string message, Exception ex, int Code)
: base(message, ex)
{
this.Code = Code;
}
}
}
- 6. satır: Sınıf, [Exception] sınıfından türetilmiştir;
- 10. satır: Bir hata kodu olan [Code] adlı bir public özelliğe sahiptir;
- uygulamamızda iki tür oluşturucu kullanacağız:
- 23-27. satırlardaki oluşturucu, aşağıda gösterildiği gibi kullanılabilir:
- (devamı)
- veya 29-33. satırlarda yer alan, meydana gelen bir istisnayı [PamException] türünde bir istisna içine kapsülleyerek yukarıya iletmeye yarayan oluşturucu:
try{
....
}catch (IOException ex){
// istisna örneğini kapsülleriz
throw new PamException("Problème d'accès aux données",ex,10);
}
Bu ikinci yöntemin avantajı, ilk istisnada yer alabilecek bilgilerin kaybolmamasıdır.
9.7.5. [métier] katmanının uygulanması
[IPamMetier] arayüzü, aşağıdaki [PamMetier] sınıfı tarafından uygulanacaktır:
using System;
using Pam.Metier.Entites;
using System.Collections.Generic;
namespace Pam.Metier.Service
{
public class PamMetier : IPamMetier
{
// önbellekteki çalışan listesi
public Employe[] Employes { get; set; }
// numaralarına göre indekslenmiş çalışanlar SS
private IDictionary<string, Employe> dicEmployes = new Dictionary<string, Employe>();
// çalışan listesi
public Employe[] GetAllIdentitesEmployes()
{
...
// çalışan listesi oluşturulur
return Employes;
}
// maaş hesaplaması
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés)
{
...
}
}
- 7. satır: [PamMetier] sınıfı, [IPamMetier] arayüzünü uygular;
- 10. satır: [PamMetier] sınıfı, çalışan listesini önbellekte tutar;
- 12. satır: Bir çalışanı sosyal güvenlik numarasıyla eşleştiren bir sözlük;
- satır 15-20: çalışan listesini döndüren yöntem;
- 23-26. satırlar: Bir çalışanın maaşını hesaplayan yöntem.
[GetAllIdentitesEmploye] yöntemi şu şekildedir:
// çalışan listesi
public Employe[] GetAllIdentitesEmployes()
{
if (Employes == null)
{
// üç çalışandan oluşan bir tablo oluşturulur
Employes = new Employe[3];
Employes[0] = new Employe()
{
SS = "254104940426058",
Nom = "Jouveinal",
Prenom = "Marie",
Adresse = "5 rue des oiseaux",
Ville = "St Corentin",
CodePostal = "49203",
Indemnites = new Indemnites() { Indice = 2, BaseHeure = 2.1, EntretienJour = 2.1, RepasJour = 3.1, IndemnitesCp = 15 }
};
dicEmployes.Add(Employes[0].SS, Employes[0]);
Employes[1] = new Employe()
{
SS = "260124402111742",
Nom = "Laverti",
Prenom = "Justine",
Adresse = "La brûlerie",
Ville = "St Marcel",
CodePostal = "49014",
Indemnites = new Indemnites() { Indice = 1, BaseHeure = 1.93, EntretienJour = 2, RepasJour = 3, IndemnitesCp = 12 }
};
dicEmployes.Add(Employes[1].SS, Employes[1]);
// sözlüğe eklenmeyecek hayali bir çalışan
// var olmayan bir çalışanı simüle etmek için
Employes[2] = new Employe()
{
SS = "XX",
Nom = "X",
Prenom = "X",
Adresse = "X",
Ville = "X",
CodePostal = "X",
Indemnites = new Indemnites() { Indice = 0, BaseHeure = 0, EntretienJour = 0, RepasJour = 0, IndemnitesCp = 0 }
};
}
// çalışan listesi görüntüleniyor
return Employes;
}
- 4. satır: Çalışan listesinin daha önce oluşturulup oluşturulmadığına bakılır;
- 7. satır: Aksi takdirde, üç çalışandan oluşan bir dizi oluşturulur;
- 8-17. satırlar: ilk çalışan;
- 18. satır: sözlüğe eklenir;
- 19-28. satırlar: ikinci çalışan;
- 29. satır: sözlüğe eklenir;
- satır 32-42: üçüncü çalışan. Bu çalışan, birazdan açıklayacağımız bir nedenden dolayı sözlüğe eklenmez.
[GetSalaire] yöntemi şu şekilde olacaktır:
// maaş hesaplaması
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés)
{
// SS numaralı çalışanı alıyoruz
Employe e = dicEmployes.ContainsKey(ss) ? dicEmployes[ss] : null;
// var mı?
if (e == null)
{
throw new PamException(string.Format("L'employé de n° SS [{0}] n'existe pas", ss), 10);
}
// hayali bir maaş bordrosu oluşturulur
return new FeuilleSalaire()
{
Employe = e,
Cotisations = new Cotisations() { CsgRds = 3.49, Csgd = 6.15, Secu = 9.38, Retraite = 7.88 },
ElementsSalaire = new ElementsSalaire() { CotisationsSociales = 100, IndemnitesEntretien = 100, IndemnitesRepas = 100, SalaireBase = 100, SalaireNet = 100 }
};
}
- 2. satır: Yöntem, maaşı hesaplanacak çalışanın SS numarasını, çalışılan saat sayısını ve çalışılan gün sayısını alır;
- 5. satır: Çalışan, sözlükten aranır. Bunlardan birinin sözlükte bulunmadığını hatırlayalım;
- satır 7-10: Çalışan bulunamazsa, [PamException] istisnası tetiklenir;
- satır 12-17: hayali bir maaş bordrosu döndürülür.
9.7.6. [métier] katmanının konsol testi
[métier] katmanının taslağı şu anda şöyledir:
![]() |
Yukarıdaki [Program] sınıfı, [IPamMetier] arayüzünün yöntemlerini test edecektir. Basit bir örnek şu şekilde olabilir:
using Pam.Metier.Entites;
using Pam.Metier.Service;
using System;
namespace Pam.Metier.Tests
{
class Program
{
public static void Main()
{
// [métier] katmanının örneği
IPamMetier pamMetier = new PamMetier();
// çalışan listesi
Employe[] employes = pamMetier.GetAllIdentitesEmployes();
Console.WriteLine("Liste des employés--------------------");
foreach (Employe e in employes)
{
Console.WriteLine(e);
}
// maaş bordro hesaplamaları
Console.WriteLine("Calculs de feuilles de salaire-----------------");
Console.WriteLine(pamMetier.GetSalaire(employes[0].SS, 30, 5));
Console.WriteLine(pamMetier.GetSalaire(employes[1].SS, 150, 20));
try
{
Console.WriteLine(pamMetier.GetSalaire(employes[2].SS, 150, 20));
}
catch (PamException ex)
{
Console.WriteLine(string.Format("PamException : {0}", ex.Message));
}
}
}
}
- 12. satır: [métier] katmanının örneklenmesi;
- 14-19. satırlar: [IPamMetier] arayüzünün [GetAllIdentitesEmploye] yönteminin test edilmesi;
- 21-31. satırlar: [IPamMetier] arayüzünün [GetSalaire] yönteminin test edilmesi.
Bu konsol programının çalıştırılması sonucunda aşağıdaki çıktılar elde edilir:
Okuyucunun bu sonuçları çalıştırılan kodla ilişkilendirmesi istenir.
Bu projeyi, oluşturacağımız web projesinde kullanabilmek için, onu bir sınıf kütüphanesi haline getiriyoruz:
![]() |
- [1] olarak, [Program.cs] dosyasının özelliklerinde;
- [2] dosyasında, dosyanın oluşturulan derleme paketinin bir parçası olmayacağı belirtilir;
- [3, 4]'te, [pam-metier-simule] projesinin özelliklerinde, [Application] [3] seçeneğinde, [4]'te, oluşturma işleminin bir sınıf kütüphanesi (DLL biçiminde) sağlaması gerektiği belirtilir.
![]() |
- [5]'te, [Release] türünde bir derleme talep edilmektedir. Diğer tür ise [Debug]'tir. Derleme, hata ayıklamayı kolaylaştıran bilgiler içerir;
- [6]'te, [pam-metier-simule] projesi oluşturulur;
![]() |
- [7]'te, çözümdeki tüm dosyalar görüntülenir;
- [8] komutunda, [bin / Release] klasöründe projemizin DLL dosyası görüntülenir.
9.8. Adım 2: Web uygulamasının kurulması
Önceki Visual Studio çözümünde, MVC adlı web katmanı projesini oluşturacağız.
![]() |
Visual Studio Express for Web ile, daha önce Visual Studio Express for Desktop ile oluşturduğumuz [pam-td] çözümünü açıyoruz.
![]() |
- [1]'te, [pam-td] çözümü Visual Studio Express for Web'e yüklendi;
- [2]'te, az önce oluşturduğumuz simüle edilmiş [métier] katmanı için çözüm ve proje.
Bu yeni adımda, web uygulamasının iskeletini oluşturacağız.
![]() |
- [1]'te, [pam-td] çözümüne yeni bir proje ekliyoruz;
![]() |
- [2]'te, ASP.NET MVC 4 adlı bir proje seçiyoruz;
- adını [pam-web-01] [3] olarak değiştirin;
- [4]'te, temel şablon olan ASP.NET MVC seçilir;
- [5]'te, oluşturulan proje;
![]() |
- [6]'te, yeni bir proje oluşturulur; bu, çözümün başlangıç projesidir ve [Ctrl-F5] komutu verildiğinde çalıştırılacak olan projedir;
- [7]'te, yeni projenin adı kalın harflerle gösterilir; bu, projenin çözümün başlangıç projesi olduğunu belirtir.
Şimdi, Windows Gezgini'ni kullanarak projenin [Content] klasörünü [étudedecas-support / web / Content] klasörüyle değiştiriyoruz. Bu işlem tamamlandıktan sonra, yeni dosyaları [pam-web-01] projesine eklememiz gerekiyor. Bunun için şu adımları izleyeceğiz:
![]() |
- [1]'e ekleyin ve çözümü yenileyin;
- [2]'e, çözümdeki tüm dosyaları görüntüleyin;
- [3]'te, [Images] adlı bir klasör görünür;
- bu klasörü [4]'te projeye dahil ediyoruz.
[Scripts] klasörüne, istemci tarafında doğrulama için gerekli olan JQuery Globalization ve [1] komut dosyalarını ekleyin.
![]() |
[_Layout.cshtml] ve [2] ana sayfalarının içeriği şu şekilde olacaktır:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script type="text/javascript" src="~/Scripts/myScripts.js"></script>
</head>
<body>
<table>
<tbody>
<tr>
<td>
<h2>Simulateur de calcul de paie</h2>
</td>
<td style="width: 20px">
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
</td>
<td>
<a id="lnkFaireSimulation" href="javascript:faireSimulation()">| Faire la simulation<br />
</a>
<a id="lnkEffacerSimulation" href="javascript:effacerSimulation()">| Effacer la simulation<br />
</a>
<a id="lnkVoirSimulations" href="javascript:voirSimulations()">| Voir les simulations<br />
</a>
<a id="lnkRetourFormulaire" href="javascript:retourFormulaire()">| Retour au formulaire de simulation<br />
</a>
<a id="lnkEnregistrerSimulation" href="javascript:enregistrerSimulation()">| Enregistrer la simulation<br />
</a>
<a id="lnkTerminerSession" href="javascript:terminerSession()">| Terminer la session<br />
</a>
</td>
</tbody>
</table>
<hr />
<div id="content">
@RenderBody()
</div>
</body>
</html>
Not: 8. satırda, jQuery sürümünü kendi Visual Studio sürümünüze göre uyarlayın.
- 7. satır: uygulamanın stil sayfasına referans;
- 8-10. satırlar: istemci tarafında doğrulama için gerekli komut dosyalarına referanslar;
- 11-12. satırlar: Fransızca virgül ile gerçek sayıların girilmesi için gerekli komut dosyalarına ilişkin referanslar;
- 13. satır: Ajax modu için gerekli komut dosyalarına ilişkin referans;
- 14. satır: uygulamaya özgü komut dosyaları;
- 24. satır: Ajax çağrılarının tamamlanmasını beklerken görüntülenen resim;
- 26-39. satırlar: altı JavaScript bağlantısı;
- 43. satır: Uygulamanın farklı görünümlerinin görüntüleneceği bölüm;
- 44. satır: uygulamanın farklı görünümlerinin gövdesi.
Ardından, uygulamanın varsayılan yolunu değiştireceğiz:
![]() |
[RouteConfig] dosyası aşağıdaki içeriğe sahip olacaktır:
using System.Web.Mvc;
using System.Web.Routing;
namespace pam_web_01
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Pam", action = "Index" }
);
}
}
}
- 14. satır: URL dosyaları, [{controller}/{action}] biçiminde olacaktır;
- 15. satır: Herhangi bir eylem yoksa, [Index] eylemi kullanılacaktır. Denetleyici yoksa, [Pam] denetleyicisi kullanılacaktır.
Bu yapılandırmaya göre, URL [/], URL [/Pam/Index] ile eşdeğerdir. Uygulamamız APU türü olduğundan, URL ve [/], bu uygulamanın tek URL'i olacaktır.
[Pam] denetleyicisini oluşturun:
[PamController] denetleyicisini şu şekilde değiştirin:
using System.Web.Mvc;
namespace Pam.Web.Controllers
{
public class PamController : Controller
{
[HttpGet]
public ViewResult Index()
{
return View();
}
}
}
- 3. satır: denetleyiciyi [Pam.Web.Controllers] ad alanına yerleştiriyoruz;
- 7. satır: [Index] eylemi yalnızca HTTP ve GET komutlarını işleyecektir;
- 8. satır: [ActionResult] türü yerine [ViewResult] türünü döndürüyoruz.
Şimdi, yukarıdaki [Index] eylemi tarafından görüntülenen [Index.cshtml] görünümünü oluşturun:
![]() |
[Index.cshtml]'i şu şekilde değiştirin:
@{
ViewBag.Title = "Pam";
}
<h2>Formulaire</h2>
Uygulamayı [Ctrl-F5] ile çalıştırın. Aşağıdaki sayfayı görmeniz gerekir:
![]() |
Ödev: Neler olduğunu açıklayın.
Uygulama, [_Layout.cshtml] ana sayfasında referans verilen bir stil sayfası kullanıyor:
<link rel="stylesheet" href="~/Content/Site.css" />
[/Content/Site.css] stil sayfası, uygulamanın sayfaları için bir arka plan görüntüsü tanımlar:
body {
background-image: url("/Content/Images/standard.jpg");
}
![]() |
9.9. 3. Adım: APU şablonunun kurulması
7.5 ve 7.6. paragraflarda açıklanan APU şablonuna (Tek Sayfalı Uygulama) göre bir uygulama yazmak istiyoruz. Tek sayfa, uygulamanın başlatılması sırasında tarayıcı tarafından yüklenen sayfadır:
![]() |
- Yukarıdaki [1] kısmı, tek sayfanın sabit kısmıdır. Bunun, [_Layout.cshtml] ana sayfası tarafından sağlandığını görmüştük;
- [2] kısmı ise tek sayfanın değişken kısmıdır. Bu kısım, ana sayfa [_Layout.cshtml]'in [content] kimlikli bölgesine yerleştirilir:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
...
<script type="text/javascript" src="~/Scripts/myScripts.js"></script>
</head>
<body>
<table>
...
</table>
<hr />
<div id="content">
@RenderBody()
</div>
</body>
</html>
Uygulamanın çeşitli sayfa parçaları, 13. satırdaki [content] kimlikli bölgede görüntülenecektir. Bunlar, Ajax çağrıları aracılığıyla görüntülenecektir. Bu çağrıları gerçekleştiren JavaScript komut dosyaları, 6. satırda referans verilen [myScripts.js] dosyasında bulunmaktadır. İhtiyacımız olacak bu dosyayı oluşturun:
![]() |
Şimdi 7.6. paragrafında açıklanan APU şablonunu takip ediyoruz. Unuttuysanız bu paragrafı tekrar okuyun. Şimdi uygulama tarafından görüntülenen çeşitli sayfa parçalarını yerleştireceğiz.
9.9.1. JavaScript geliştirici araçları
Chrome tarayıcısında, sayfalarınızın HTML, CSS ve JavaScript kodlarını hata ayıklamak için bir dizi araca sahip olduğunuzu hatırlatırız. Bu araçlar kısmen 7.2. paragrafında tanıtılmıştı. APU modelinde, tarayıcılar uygulamanın ilk sayfasında referans verilen JavaScript komut dosyalarını önbellekte tutar. Bu nedenle, komut dosyalarınızı değiştirdiğinizde bu önbelleği temizlemeyi unutmamalısınız; aksi takdirde değişiklikler dikkate alınmayabilir. Chrome'da bunu şu şekilde yapabilirsiniz:
- Geliştirme ortamını görüntülemek için [Ctrl-Maj-I] komutunu çalıştırın
![]() |
- geliştirme penceresinin sağ alt köşesindeki [1] simgesine tıklayın;
- ardından geliştirme modunda önbelleği devre dışı bırakan [2] seçeneğini işaretleyin.
9.9.2. Formu görüntülemek için kısmi görünümün kullanılması
Giriş formu, uygulama tarafından görüntülenen parçalardan biridir. Şu anda bu form, tam bir görünüm olan [Index.cshtml] görünümü tarafından görüntülenmektedir:
@{
ViewBag.Title = "Pam";
}
<h2>Formulaire</h2>
Bu görünüm, [Index] eylemi tarafından görüntülenir:
[HttpGet]
public ViewResult Index()
{
return View();
}
Yukarıdaki 4. satırda, görüntülenenin [View] tam görünümü olduğu ve [PartialView] kısmi görünümü olmadığı açıkça görülmektedir. Sayfa parçası olacak form için bir kısmi görünüme ihtiyacımız var. [Index.cshtml] görünümünü şu şekilde değiştiriyoruz:
@{
ViewBag.Title = "Pam";
}
@Html.Partial("Formulaire")
4. satırda, form artık [Index.cshtml] sayfasının bir parçası değildir. Artık [Formulaire.cshtml] adlı bir kısmi görünümde yer almaktadır:
![]() |
[Formulaire.cshtml] kodunun içeriği şöyledir:
<h2>Formulaire</h2>
Bu değişiklikleri yapın ve uygulamayı başlattığınızda hala aşağıdaki ekranın görüntülendiğinden emin olun:
![]() |
9.9.3. [faireSimulation] Ajax çağrısı
Kullanıcının [Faire la simulation] bağlantısına tıkladığında görüntülenen parçaya odaklanıyoruz:
![]() |
- [1]'te, kullanıcı [Faire la simulation] bağlantısına tıklar;
- [2]'te, simülasyon formun altında görünür.
Formu görüntüleyen [Formulaire.cshtml] kısmi görünümünü şu şekilde geliştiriyoruz:
<h2>Formulaire</h2>
<div id="simulation" />
3. satırda, simülasyon parçasını barındırmak için [simulation] kimliğine sahip bir bölge oluşturuyoruz.
Aşağıdaki [Simulation.cshtml] kısmi görünümünü oluşturuyoruz:
![]() |
[Simulation.cshtml] görünümünün içeriği şöyledir:
<hr />
<h2>Simulation</h2>
Şimdi, [Faire la simulation] bağlantısına yapılan tıklamayı yöneten JavaScript kodunu yazmamız gerekiyor. 7.6.5. paragrafında açıklanan yaklaşımı izleyeceğiz. Öncelikle, [_Layout.cshtml] içindeki bağlantının HTML koduna bakalım:
<a id="lnkFaireSimulation" href="javascript:faireSimulation()">| Faire la simulation<br />
</a>
Görüldüğü gibi, [Faire la simulation] bağlantısına tıklandığında JS [faireSimulation] fonksiyonunun çalıştırılması başlatılacaktır. Bu işlev, uygulama için gerekli olan diğer JS işlevleriyle birlikte [myScripts.js] dosyasına yazılacaktır:
// genel değişkenler
var loading;
var content;
function faireSimulation() {
// Ajax çağrısı manuel olarak yapılır
...
}
function effacerSimulation() {
// formdaki girdileri siliyoruz
...
}
function enregistrerSimulation() {
// manuel olarak Ajax çağrısı yapılıyor
...
}
function voirSimulations() {
// manuel olarak Ajax çağrısı yapılıyor
...
}
function retourFormulaire() {
// manuel olarak Ajax çağrısı yapılıyor
...
}
function terminerSession() {
...
}
// belge yüklendiğinde
$(document).ready(function () {
// sayfanın çeşitli bileşenlerinin referansları alınır
loading = $("#loading");
content = $("#content");
});
- 35-39. satırlar: Uygulamanın başlatılması sırasında yürütülen JQuery işlevi;
- 37-38. satırlar: 2. ve 3. satırlardaki küresel değişkenler başlatılır.
[loading] ve [content] kimlikli öğelerin, [_Layout.cshtml] ana sayfasında (aşağıdaki 14. ve 21. satırlar) tanımlandığını hatırlatırız:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<table>
<tbody>
<tr>
<td>
<h2>Simulateur de calcul de paie</h2>
</td>
<td style="width: 20px">
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
</td>
...
</td>
</tbody>
</table>
<hr />
<div id="content">
@RenderBody()
</div>
</body>
</html>
Görev: 7.6.5 numaralı paragrafta açıklanan yöntemi izleyerek JS ve [faireSimulation] fonksiyonlarını yazınız. Bu fonksiyon, POST türünde bir Ajax çağrısı göndererek [/Pam/FaireSimulation] eylemini tetikleyecektir. Şu an için gönderilen veri olmayacaktır.[/Pam/FaireSimulation] eylemi, [Simulation.cshtml] kısmi görünümünü JS [faireSimulation] işlevine geri gönderecek ve bu işlev de HTML akışını [simulation] kimliğine sahip bölgeye yerleştirecektir.
Uygulamanızdaki [Faire la simulation] bağlantısını test edin.
9.9.4. [enregistrerSimulation] Ajax çağrısı
[Enregistrer la simulation] bağlantısı, [_Layout.cshtml] içinde şu şekilde tanımlanmıştır:
<a id="lnkEnregistrerSimulation" href="javascript:enregistrerSimulation()">| Enregistrer la simulation<br />
</a>
Görev: Önceki adımları izleyerek, JS [enregistrerSimulation] işlevini yazın. Bu işlev, POST türünde bir Ajax çağrısını [/Pam/EnregistrerSimulation] eylemine gönderecektir. Şu an için gönderilecek veri olmayacaktır.[/Pam/EnregistrerSimulation] eylemi, [Simulations.cshtml] kısmi görünümünü JS [enregistrerSimulation] işlevine geri gönderecek ve bu işlev de HTML akışını [content] kimlikli ana sayfanın bölgesine yerleştirecektir.
[Simulations.cshtml] görünümü şu şekildedir:
![]() |
İçeriği şöyledir:
<h2>Simulations</h2>
İşte bir yürütme örneği:
![]() | ![]() |
9.9.5. [voirSimulations] Ajax çağrısı
[Voir les simulations] bağlantısı, [_Layout.cshtml] içinde şu şekilde tanımlanmıştır:
<a id="lnkVoirSimulations" href="javascript:voirSimulations()">| Voir les simulations<br />
</a>
Ödev: Yukarıdaki adımları izleyerek, JS [voirSimulations] işlevini yazın. Bu işlev, POST türünde bir Ajax çağrısını [/Pam/VoirSimulations] eylemine gönderecektir. Şu an için gönderilecek veri olmayacaktır.[/Pam/VoirSimulations] eylemi, [Simulations.cshtml] kısmi görünümünü JS ve [voirSimulations] işlevlerine geri gönderecek; bu işlevler de HTML akışını [content] kimliğine sahip ana sayfanın bölgesine yerleştirecektir.
[Simulations.cshtml] görünümü, önceki soruda da kullanılan görünümdür.
İşte bir yürütme örneği:
![]() |
9.9.6. [retourFormulaire] Ajax çağrısı
[Retour au formulaire de simulation] bağlantısı, [_Layout.cshtml] içinde şu şekilde tanımlanmıştır:
<a id="lnkRetourFormulaire" href="javascript:retourFormulaire()">| Retour au formulaire de simulation<br />
</a>
Ödev: Yukarıdaki adımları izleyerek, JS [retourFormulaire] işlevini yazın. Bu işlev, POST türünde bir Ajax çağrısını [/Pam/Formulaire] eylemine gönderecektir. Şu an için gönderilecek veri olmayacaktır.[/Pam/Formulaire] eylemi, [Formulaire.cshtml] kısmi görünümünü JS [retourFormulaire] işlevine geri gönderecek ve bu işlev de HTML akışını [content] kimliğine yerleştirir.
[Formulaire .cshtml] görünümü zaten tanımlanmıştır. İşte bir yürütme örneği:
![]() |
9.9.7. [terminerSession] Ajax çağrısı
[Terminer la session] bağlantısı, [_Layout.cshtml] içinde şu şekilde tanımlanmıştır:
<a id="lnkTerminerSession" href="javascript:terminerSession()">| Terminer la session<br />
</a>
Ödev: Yukarıdaki adımları izleyerek JS [terminerSession] işlevini yazın. Bu işlev, POST türünde bir Ajax çağrısını [/Pam/TerminerSession] eylemine gönderecektir. Şu an için gönderilen veri olmayacaktır.[/Pam/TerminerSession] eylemi, [Formulaire.cshtml] kısmi görünümünü JS [terminerSession] işlevine geri gönderecek ve bu işlev de HTML akışını [content] kimliğine sahip ana sayfanın bölgesine yerleştirecektir.
İşte bir yürütme örneği:
![]() |
9.9.8. JS [effacerSimulation] işlevi
[Effacer la simulation] bağlantısı, [_Layout.cshtml] içinde şu şekilde tanımlanmıştır:
<a id="lnkEffacerSimulation" href="javascript:effacerSimulation()">| Effacer la simulation<br />
</a>
JS [effacerSimulation] işlevinin amacı şudur:
- [Simulation] parçası varsa onu gizlemek;
- formdaki giriş alanlarını uygulamanın ilk yüklendiği andaki durumuna geri döndürmek (giriş alanları olduğunda - şu anda yok).
Görev: JS [effacerSimulation] işlevini yazın. Burada Ajax çağrısı yoktur. Olanlar tarayıcı içinde gerçekleşir ve sunucuyu ilgilendirmez.
İşte bir çalıştırma örneği:
![]() |
9.9.9. Ekranlar Arası Gezinme Yönetimi
Şu anda bağlantılar her zaman görüntüleniyor. Şimdi bir JavaScript işleviyle bunların görüntülenmesini yöneteceğiz. Öncelikle, [_Layout.cshtml] içindeki altı JavaScript bağlantısının kodunu hatırlayalım:
<a id="lnkFaireSimulation" href="javascript:faireSimulation()">| Faire la simulation<br />
</a>
<a id="lnkEffacerSimulation" href="javascript:effacerSimulation()">| Effacer la simulation<br />
</a>
<a id="lnkVoirSimulations" href="javascript:voirSimulations()">| Voir les simulations<br />
</a>
<a id="lnkRetourFormulaire" href="javascript:retourFormulaire()">| Retour au formulaire de simulation<br />
</a>
<a id="lnkEnregistrerSimulation" href="javascript:enregistrerSimulation()">| Enregistrer la simulation<br />
</a>
<a id="lnkTerminerSession" href="javascript:terminerSession()">| Terminer la session<br />
</a>
Tüm bağlantıların, bunları JavaScript ile yönetmemizi sağlayacak bir [id] özniteliği vardır. Sayfanın yüklenmesi sırasında çalıştırılan JS yöntemini şu şekilde değiştiriyoruz:
// genel değişkenler
var loading;
var content;
var lnkFaireSimulation;
var lnkEffacerSimulation
var lnkEnregistrerSimulation;
var lnkTerminerSession;
var lnkVoirSimulations;
var lnkRetourFormulaire;
var options;
...
// belge yüklendiğinde
$(document).ready(function () {
// sayfanın çeşitli bileşenlerinin referansları alınır
loading = $("#loading");
content = $("#content");
// menüdeki bağlantılar
lnkFaireSimulation = $("#lnkFaireSimulation");
lnkEffacerSimulation = $("#lnkEffacerSimulation");
lnkEnregistrerSimulation = $("#lnkEnregistrerSimulation");
lnkVoirSimulations = $("#lnkVoirSimulations");
lnkTerminerSession = $("#lnkTerminerSession");
lnkRetourFormulaire = $("#lnkRetourFormulaire");
// bunlar bir tabloya eklenir
options = [lnkFaireSimulation, lnkEffacerSimulation, lnkEnregistrerSimulation, lnkVoirSimulations, lnkTerminerSession, lnkRetourFormulaire];
// sayfanın bazı öğelerini gizliyoruz
loading.hide();
// menüyü sabitliyoruz
setMenu([lnkFaireSimulation, lnkVoirSimulations, lnkTerminerSession]);
});
- 19-24. satırlar: Altı bağlantının referanslarını alıyoruz. Bu referanslar, 4-9. satırlarda küresel değişkenler olarak tanımlanıyor;
- 26. satır: [options] dizisi, altı referansla başlatılır. Bu dizi, 10. satırda global değişken olarak tanımlanmıştır;
- 28. satır: Ajax çağrılarının tamamlanmasını beklerken animasyonlu resim gizlenir;
- satır 30: [lnkFaireSimulation, lnkVoirSimulations, lnkTerminerSession] bağlantıları görüntülenir. Diğerleri gizlenecektir.
JS [setMenu] işlevi şu şekildedir:
function setMenu(show) {
// tablodaki bağlantıları görüntülenir [show]
...
}
Görev: JS ve [setMenu] fonksiyonlarını yazınız.
T bir bağlantı dizisiyse:
- T.length, bağlantı sayısıdır;
- T[i], i numaralı bağlantıdır;
- T[i].show(), i numaralı bağlantıyı görüntüler;
- T[i].hide(), i numaralı bağlantıyı gizler.
Bu yeni JS işlevleriyle, başlangıçta görüntülenen sayfa şöyledir:
![]() |
Aşağıdaki ekranları elde etmek için JS ve [faireSimulation, effacerSimulation, enregistrerSimulation, voirSimulations, retourFormulaire, terminerSession] işlevlerini uyarlayın:
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
Artık APU şablonu ve gezinme bağlantıları hazır olduğuna göre, sunucu tarafındaki eylemleri ve görünümleri yazmaya geçebiliriz. Adımları ilerledikçe, şu anda çalışan bazı Ajax bağlantılarının artık çalışmadığını fark edeceksiniz; bunun nedeni, istemciye gönderilen kısmi görünümleri değiştireceğinizdir. Sunucu tarafındaki çeşitli eylemleri ve görünümleri oluşturdukça, istemci tarafındaki Ajax bağlantıları da size verdiğiniz işlevselliği yeniden kazanacaktır.
9.10. Adım 4: [Index] sunucu eyleminin yazılması
Şu anda uygulamanın başlatılmasıyla birlikte aşağıdaki ekran karşımıza çıkıyor:
![]() |
Bu ekranın yerine şunu görmek istiyoruz:
![]() |
Bu sayfayı oluşturması gereken eylem [Index]'tir. Birkaç noktaya değinelim:
- Sayfada üç giriş alanı içeren bir form bulunmaktadır:
- maaş hesaplanacak çalışan,
- çalıştığı saat sayısı,
- çalıştığı gün sayısı;
- form, [Faire la simulation] bağlantısı aracılığıyla gönderilir;
- [Heures travaillées] ve [Jours travaillés] giriş alanlarının geçerliliği kontrol edilmelidir;
- çalışan listesi, daha önce oluşturduğumuz [métier] katmanından alınır.
[Index] eyleminin mevcut kodunu hatırlayalım:
[HttpGet]
public ViewResult Index()
{
return View();
}
Bu eylemin görüntülediği [Index.cshtml] görünümünün kodu:
@{
ViewBag.Title = "Pam";
}
@Html.Partial("Formulaire")
ve [Formulaire.cshtml] kısmi görünümünün kodu:
<h2>Formulaire</h2>
Bu üç yerde değişiklikler yapılacaktır.
9.10.1. Form şablonu
URL [/Pam/Index] işleme zincirine geri dönelim:
![]() |
- Müşterinin HTTP numaralı talebi, [1] olarak gelir;
- [2] aşamasında, istekte yer alan bilgiler, [3] eylem şablonuna dönüştürülür ve bu şablon, [4] eylemine girdi olarak kullanılır;
- [4] aşamasında, eylem bu şablondan yola çıkarak bir yanıt oluşturacaktır. Bu yanıtın iki bileşeni olacaktır: bir V görünümü ([6]) ve bu görünümün M şablonu ([5]);
- V [6] görünümü, müşteriye yönelik HTTP yanıtını oluşturmak için kendi M modeli olan [5]'i kullanacaktır.
İlgilendiğimiz eylem, şu anda aşağıdaki gibi olan [Index] eylemidir:
[HttpGet]
public ViewResult Index()
{
return View();
}
[Index] eylemi, [Index.cshtml] görünümüne herhangi bir şablon aktarmamaktadır. Dolayısıyla bu görünüm, çalışan listesini görüntüleyemeyecektir. Bu liste, [métier] katmanından talep edilebilir. Bunun için, [pam-web-01] projesinin [pam-metier-simule] projesine bir referansı olması gerekir. Şimdi bu referansı oluşturuyoruz:
![]() |
- [1]'te, [pam-web-01] projesindeki [References] öğesine sağ tıklayıp ardından [Ajouter une référence]'i seçin;
- [2]'e dönüştürmek için, [Solution] seçeneğini seçin, ardından [pam-metier-simule] projesini [3]'e dönüştürün;
- [4]'te, [pam-metier-simule] projesi, [pam-web-01] projesinin referanslarına eklenmiştir.
9.10.2. Uygulama şablonu
Uygulama modeli ve oturum modeli gibi önemli kavramları 78. sayfadaki 4.10 numaralı paragrafta tanıtmıştık. Şimdi bunları kullanacağız. Hatırlatmak gerekirse, modele şunlar eklenir:
- tüm kullanıcılar için salt okunur veriler. Bu model, tüm kullanıcıların tüm istekleri tarafından paylaşılan bir bellek oluşturur;
- belirli bir kullanıcı için okuma ve yazma erişimine sahip veriler içeren oturum modeli. Bu model, söz konusu kullanıcının tüm istekleri tarafından paylaşılan bir bellek oluşturur.
Uygulama modeline ne ekleyeceğiz? Uygulamanın mimarisine geri dönelim:
![]() |
[web] katmanı, [métier] katmanına bir referans içerir. Bu referans tüm kullanıcılar tarafından paylaşılabilir. Dolayısıyla bunu uygulama modeline ekleyebiliriz. Ayrıca, çalışan listesinin değişmeyeceği varsayımında bulunacağız. Bu liste tek sefer okunup tüm kullanıcılar arasında paylaşılabilir. Bu durumda şu uygulama modelini öneriyoruz:
![]() |
[ApplicationModel] sınıfının kodu şu şekilde olabilir:
using Pam.Metier.Entites;
using Pam.Metier.Service;
namespace PamWeb.Models
{
public class ApplicationModel
{
// --- uygulama kapsamındaki veriler ---
public Employe[] Employes { get; set; }
public IPamMetier PamMetier { get; set; }
}
}
Bir görünümde açılır liste görüntülemek için aşağıdaki gibi bir kod yazılır:
<!-- açılır liste -->
<tr>
<td>Liste déroulante</td>
<td>@Html.DropDownListFor(m => m.DropDownListField,
new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
</td>
</tr>
[DropDownListFor] yöntemi, ikinci parametre olarak yukarıda [SelectList] türüyle sağlanmış olan SelectListItem[] türünü bekler. Çalışan listesini içeren böyle bir dizi oluşturmamız gerekiyor. Çalışanlar değişmediğinden, bu dizi de uygulama şablonuna yerleştirilebilir. Şablonu şu şekilde değiştiriyoruz:
using Pam.Metier.Entites;
using Pam.Metier.Service;
using System.Web.Mvc;
namespace Pam.Web.Models
{
public class ApplicationModel
{
// --- uygulama kapsamı verileri ---
public Employe[] Employes { get; set; }
public IPamMetier PamMetier { get; set; }
public SelectListItem[] EmployesItems { get; set; }
}
}
Bu şablon ne zaman oluşturulmalıdır? Bunu 4.10. paragrafta göstermiştik. [Global.asax] dosyasındaki [Application_Start] yönteminin yürütülmesi sırasında:
![]() |
[Application_Start] yöntemi şu anda şu şekildedir:
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace pam_web_01
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Bunu şu şekilde değiştiriyoruz:
using Pam.Metier.Entites;
using Pam.Metier.Service;
using PamWeb.Infrastructure;
using PamWeb.Models;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace pam_web_01
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// ----------Otomatik olarak oluşturuldu
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// -------------------------------------------------------------------
// ---------- özel yapılandırma
// -------------------------------------------------------------------
// uygulama kapsamı verileri
ApplicationModel application = new ApplicationModel();
Application["data"] = application;
// [métier] katmanının örneklenmesi
application.PamMetier = ...
// çalışan tablosu
application.Employes = ...
// çalışanlar açılır menüsü öğeleri
application.EmployesItems = ...
// [ApplicationModel] için model bağlayıcı
...
}
}
}
Görev: [Application_Start] yönteminin kodunu tamamlayın. İhtiyacınız olan her şey 4.10 numaralı paragrafta yer almaktadır. Bu uzun ama önemli paragrafı dikkatlice tekrar okuyun.
33. satır aslında birkaç satırdan oluşmaktadır. [SelectListItem] türünde bir nesne oluşturmak için aşağıdaki yöntemi kullanabilirsiniz:
new SelectListItem() { Text = unTexte, Value = uneValeur };
Bu [SelectListItem], aşağıdaki HTML <option> etiketini oluşturmak için kullanılacaktır:
açılır listeden. Şu şekilde ayarlanacaktır:
- unTexte, çalışanın adı ve soyadını içersin;
- uneValeur, çalışanın SS numarası olsun.
Yukarıdaki 35. satırda, sayfa 82, paragraf 4.10'da açıklanan [ApplicationModelBinder] sınıfına ihtiyacınız olacaktır:
![]() |
9.10.3. [Index] eylem kodu
Artık uygulama için bir şablon tanımladığımıza göre, [Index] eylem kodunu şu şekilde değiştirebiliriz:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
return View();
}
- 4. satır: Uygulama şablonu artık [Index] eyleminin bir parametresidir. Bu parametrenin çerçeve tarafından nasıl başlatıldığını 4.10. paragrafta açıklamıştık.
9.10.4. [Index.cshtml] görünümünün modeli
Artık [Index] eylemi, uygulama modelinde kayıtlı çalışanlara erişebilir. Şimdi bu verileri, görüntüleyeceği [Index.cshtml] görünümüne aktarması gerekir. [ApplicationModel] türünü [Index.cshtml] görünüm modeline aktarabilirdik, ancak bu görünümün [ApplicationModel]'te bulunmayan başka bilgilere de ihtiyacı olduğunu hemen göreceğiz. Aşağıdaki [IndexModel] görünüm şablonunu kullanacağız:
![]() |
namespace Pam.Web.Models
{
public class IndexModel
{
// uygulama kapsamı verileri
public ApplicationModel Application { get; set; }
}
}
- 6. satır: [IndexModel], uygulama şablonunu yükler.
[Index] eylemi şu şekilde olur:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
return View(new IndexModel() { Application = application });
}
- 4. satırda, varsayılan görünüm [Index.cshtml], uygulama modelinin verileriyle başlatılmış [IndexModel] türü bir model kullanılarak görüntülenir.
[Index.cshtml] görünümünün bir form görüntülemesi gerektiğini biliyoruz:

Bir isteğin işleme zincirine geri dönelim:
![]() |
[GET /Pam/Index] isteği için:
- eylem [Index]'tir;
- bu eylemin şablonu [ApplicationModel]'tir;
- görünüm [Index.cshtml]'tir;
- bu görünümün şablonu [IndexModel]'tir.
Form gönderildiğinde benzer bir işleme dizisi olacaktır:
- eylem, POST'i işleyen eylemdir;
- şablonu, gönderilen değerleri bir araya getirir; burada:
- seçilen çalışanın SS numarası;
- çalışılan saat sayısı;
- çalışılan gün sayısı;
Bu üç değeri bir araya getiren bir eylem şablonu oluşturulabilir. Formu görüntülemek için kullanılan şablonu yeniden kullanmak da yaygın bir uygulamadır. Burada da bunu yapacağız. [IndexModel] sınıfı şu şekilde değişir:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Pam.Web.Models
{
[Bind(Exclude = "Application")]
public class IndexModel
{
// uygulama kapsamı verileri
public ApplicationModel Application { get; set; }
// gönderilen değerler
[Display(Name = "Employé")]
public string SS { get; set; }
[Display(Name = "Heures travaillées")]
[UIHint("Decimal")]
public double HeuresTravaillées { get; set; }
[Display(Name = "Jours travaillés")]
public double JoursTravaillés { get; set; }
}
}
- 13., 16. ve 18. satırlar: gönderilen üç değer. [joursTravaillés]'in [double] türü olarak tanımlandığına dikkat edilmelidir; oysa aslında bir tamsayı beklenmektedir. [double] türü, bu alanın istemci tarafında doğrulanmasını kolaylaştırmak için eklenmiştir; zira [int] türünün doğrulanmasında sorun yaşanmıştı;
- 12., 14. ve 17. satırlar: modele bağlı görünümdeki [Html.LabelFor] yöntemleri için açıklamalar;
- 15. satır: [HeuresTravaillées] alanının iki ondalık basamakla görüntülenmesi için bir açıklama;
- 5. satır: [Application] adlı özelliğin gönderilen değerler arasında yer almadığı belirtilmiştir.
9.10.5. [Index.cshtml] ve [Formulaire.cshtml] görünümleri
[Index.cshtml] görünümü, aşağıdaki [Index] eylemi tarafından görüntülenir:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
return View(new IndexModel() { Application = application });
}
İlginç bir şekilde, [Index.cshtml] görünümü değişmeden kalır:
@{
ViewBag.Title = "Pam";
}
@Html.Partial("Formulaire")
- görünüm herhangi bir şablon tanımlamamaktadır;
- 4. satır: [Formulaire.cshtml] kısmi görünümünü entegre eder, ancak yine bu görünüme herhangi bir şablon aktarılmaz. Testler sırasında, [IndexModel] şablonunun [Index.cshtml] görünümüne aktarılmasının, dolaylı olarak [Formulaire.cshtml] kısmi görünümüne de yayıldığı tespit edildi. Bu son görünüm artık şu şekilde olabilir:
@model Pam.Web.Models.IndexModel
@using (Html.BeginForm("FaireSimulation", "Pam", FormMethod.Post, new { id = "formulaire" }))
{
<table>
<thead>
<tr>
...
</tr>
</thead>
<tbody>
<tr>
...
</tr>
<tr>
...
</tr>
</tbody>
</table>
}
<div id="simulation" />
- 1. satır: görünüm, [IndexModel] türünde bir şablon alır;
- 3. satır: form;
- 6-10. satırlar: giriş tablosunun başlıkları;
- 12-14. satırlar: giriş satırı;
- satır 15-17: olası hata mesajları.
Görev: [Formulaire.cshtml] görünümünün kodunu tamamlayın. 5.7. paragrafında açıklanan [DropDownListFor, EditorFor, LabelFor, ValidationMessageFor] yöntemlerini kullanın.
9.10.6. [Index] eyleminin testi
URL ve [/Pam/Index] işleme zincirinin tüm öğelerini yazdık:
![]() |
Uygulamayı [Ctrl-F5] ile test ediyoruz:
![]() | ![]() |
Açılır listenizin, simüle edilmiş [métier] katmanında tanımladığımız çalışan listesiyle doğru şekilde doldurulduğunu kontrol etmelisiniz.
9.11. Adım 5: Girişlerin doğrulanmasının ayarlanması
9.11.1. Sorun
Bu konuda herhangi bir işlem yapmamış olsak da, istemci tarafında doğrulamalar halihazırda devreye girmiştir:
![]() |
![]() |
Uygulamanın [Web.config] dosyasında yer alan aşağıdaki 3. satır nedeniyle, istemci tarafında doğrulama varsayılan olarak devreye girmiştir.
<appSettings>
...
<add key="ClientValidationEnabled" value="true" />
</appSettings>
Ancak [IndexModel] dosyasında, [JoursTravaillés] alanı [double] türü olarak tanımlandığından:
public double JoursTravaillés { get; set; }
bu alana bir gerçek sayı girebiliriz:
![]() |
Ayrıca, her iki alana da rastgele değerler girilebilir:
![]() |
Formun [IndexModel] şablonu şu anda şöyledir:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Pam.Web.Models
{
[Bind(Exclude = "Application")]
public class IndexModel
{
// uygulama kapsamı verileri
public ApplicationModel Application { get; set; }
// gönderilen değerler
[Display(Name = "Employé")]
public string SS { get; set; }
[Display(Name = "Heures travaillées")]
[UIHint("Decimal")]
public double HeuresTravaillées { get; set; }
[Display(Name = "Jours travaillés")]
public double JoursTravaillés { get; set; }
}
}
Görev: Bu şablonu şu şekilde iyileştirin:
- kişiselleştirilmiş hata mesajları alabilmek;
- [HeuresTravaillées] alanı için [0,400] aralığındaki gerçek değerleri kabul etmek;
- [JoursTravaillées] alanı için [0,31] aralığındaki tamsayı değerleri kabul etmek;
7.6.2. paragrafındaki örnekten yararlanılabilir. Çalışılan gün sayısının bir tamsayı olduğunu doğrulamak için bir düzenli ifade kullanılabilir (bkz. 5.9.1. paragrafındaki örnekler).
Beklenen değerlere ilişkin örnekler şunlardır:
![]() |
![]() |
![]() |
9.11.2. Fransızca formatında gerçek sayıların girilmesi
Uygulamanın mevcut sürümünde, çalışılan saat sayısı İngiliz formatında (ondalık nokta ile) bir ondalık sayı olmalıdır. Virgül içeren Fransız formatı kabul edilmez:
![]() |
Bu sorun tespit edilmiş ve 6.1. paragrafta ele alınmıştır.
Görev: Yukarıda belirtilen paragrafta açıklanan yöntemi izleyerek, gerçek sayıların Fransızca ondalık biçiminde girilebilmesi için gerekli değişiklikleri yapın. Uygulamanızı test edin.
Şimdi, önceki ekran şu şekilde görünür:
![]() |
9.11.3. Javascript bağlantısı ile formun doğrulanması [Faire la simulation]
Şu anda, aşağıdaki dizide gösterildiği gibi geçersiz değerler gönderilebiliyor:
![]() |
![]() |
[1]'teki simülasyonun varlığı ve [2]'teki menü değişikliği, [Faire la simulation] bağlantısına tıklandığında, girilen değerler geçersiz olmasına rağmen formun gönderildiğini göstermektedir. Bu sorun, 7.6.5. paragrafında tespit edilmiş ve çözülmüştür.
Görev: Yukarıda belirtilen paragrafta açıklanan yöntemi izleyerek, girilen değerler geçersiz olduğunda [Faire la simulation] bağlantısının POST'e yönlendirilmesini engelleyin. Değişikliklerinizi test etmeden önce tarayıcınızın önbelleğini temizlemeyi unutmayın.
[Formulaire.cshtml] kısmi görünümünün, [formulaire] kimliğine sahip bir HTML formu oluşturduğunu hatırlatırız (aşağıdaki 1. satır):
@using (Html.BeginForm("FaireSimulation", "Pam", FormMethod.Post, new { id = "formulaire" }))
{
...
}
Bu durum, tarayıcıda formun kaynak kodunu görüntüleyerek doğrulanabilir:
<div id="content">
<form action="/Pam/FaireSimulation" id="formulaire" method="post">
...
</form>
<div id="simulation" />
</div>
9.12. 6. Adım: Simülasyon yapmak
9.12.1. Sorun
Simülasyon yaptığımızda, şu sonucu elde etmek istiyoruz:
![]() |
[Simulation.cshtml] kısmi görünümü artık bir çalışanın maaş bordrosunu gösteriyor.
9.12.2. [Simulation.cshtml] görünümünün yazılması
[Simulation.cshtml] görünümü şu şekilde değişir:
@model Pam.Metier.Entites.FeuilleSalaire
<hr />
<p><span class="info">Informations Employé</span></p>
<table>
<tbody>
<tr>
<td><span class="libellé">Nom</span>
</td>
<td><span class="libellé">Prénom</span>
</td>
<td><span class="libellé">Adresse</span>
</td>
</tr>
<tr>
<td>
<span class="valeur">@Model.Employe.Nom</span>
</td>
...
</tr>
<tr>
<td><span class="libellé">Ville</span>
</td>
<td><span class="libellé">Code Postal</span>
</td>
<td><span class="libellé">Indice</span>
</td>
</tr>
<tr>
...
</tr>
</tbody>
</table>
<br />
<p><span class="info">Informations Cotisations</span></p>
<table>
...
</tbody>
</table>
<br />
<p><span class="info">Informations Indemnités</span></p>
<table>
...
</table>
<br />
<p><span class="info">Informations Salaire</span></p>
<table>
...
</table>
<br />
<table>
...
</table>
- 1. satır: [Simulation.cshtml] görünümü, 9.7.3. paragrafta tanımlanan [FeuilleSalaire] türünü şablon olarak kullanır;
- bu görünüm, [Content / Site.css] uygulamasının stil sayfasında tanımlanan [libellé, info, valeur] sınıflarını kullanır:
.libellé {
background-color: azure;
margin: 5px;
padding: 5px;
}
.info {
background-color: antiquewhite;
margin: 5px;
padding: 5px;
}
.valeur {
background-color: beige;
padding: 5px;
margin: 5px;
}
Ayrıca, yine [Site.css] dosyasında, [simulation] kimlikli bölgedeki çeşitli HTML tablolarının satır yükseklikleri belirlenir; bu bölge, maaş bordrosunun görüntülendiği yerdir:
#tablo simülasyonu {
height: 30px;
}
Görev: [Simulation.cshtml] görünümünü tamamlayın.
Bir para tutarının euro cinsinden değerini görüntülemek için [string.Format] yöntemi kullanılacaktır:
Yukarıdaki komut, [somme]'i, [C] (Para Birimi) olarak ve [C2]'te belirtildiği gibi iki ondalık basamakla görüntüler.
Bu görünümü test etmek için bir maaş bordrosu sağlanmalıdır. Bu bordro, [Faire la simulation] bağlantısının Ajax çağrısının hedefi olan [/Pam/FaireSimulation] eylemi tarafından sağlanmalıdır. Şu anda bu eylem şu şekildedir:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
return View(new IndexModel() { Application = application });
}
// simülasyon yapma
[HttpPost]
public PartialViewResult FaireSimulation()
{
return PartialView("Simulation");
}
Yukarıda, [FaireSimulation] eylemi, [Simulation.cshtml] görünümüne herhangi bir şablon aktarmamaktadır. Bu eylemin, görünümüne bir maaş bordrosu aktarması gerekmektedir. Maaş bordrolarının hesaplamasını yapan katmanın [métier] olduğu bilinmektedir. Bu [métier] katmanına, 9.10.2 numaralı paragrafta tanımladığımız [ApplicationModel] uygulama şablonu aracılığıyla erişilebilir:
public class ApplicationModel
{
// --- uygulama kapsamı verileri ---
public Employe[] Employes { get; set; }
public IPamMetier PamMetier { get; set; }
public SelectListItem[] EmployesItems { get; set; }
}
[métier] katmanına, yukarıdaki 5. satırdaki özellik aracılığıyla erişilebilir. [FaireSimulation] eyleminin [métier] katmanına erişebilmesi için, [Index] eylemi için yaptığımız gibi bu eyleme de uygulama şablonunu aktaracağız. Kod bu durumda şu şekilde değişir:
// simülasyon yap
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application)
{
return PartialView("Simulation");
}
Artık eylem içinde hayali bir maaş bordrosu hesaplayabiliyoruz. Kod şu şekilde değişir:
// simülasyon yap
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application)
{
FeuilleSalaire feuilleSalaire = application.PamMetier.GetSalaire("254104940426058", 150, 20);
return PartialView("Simulation", feuilleSalaire);
}
- 5. satırda, hayali bir maaş hesaplanıyor. İlk parametre, mevcut bir SS numarasıdır. Bu numara, 9.7.5. paragrafta simüle edilen [métier] sınıfında tanımlanmıştır. İkinci parametre çalışılan saat sayısı, üçüncü parametre ise çalışılan gün sayısıdır;
- 6. satır: Bu maaş bordrosu, [Simulation.cshtml] görünümüne şablon olarak aktarılır.
Artık [Simulation.cshtml] görünümünü test etmeye hazırız:
![]() |
Herhangi bir giriş yapmadan simülasyonu talep ediyoruz. Bunun üzerine şu sonucu elde ediyoruz:
![]() |
9.12.3. Gerçek maaş hesaplaması
Mevcut [FaireSimulation] eylemi her zaman aynı maaş bordrosunu hesaplıyor:
// simülasyon yap
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application)
{
FeuilleSalaire feuilleSalaire = application.PamMetier.GetSalaire("254104940426058", 150, 20);
return PartialView("Simulation", feuilleSalaire);
}
Bu işlem, girilen bilgileri dikkate almaz:
- maaş hesaplaması yapılan çalışan;
- çalıştığı saat sayısı;
- çalıştığı gün sayısı.
Girilen değerler, [FaireSimulation] eylemine şu şekilde ulaşır:
- kullanıcı [Faire la simulation] bağlantısına tıklar. Bu, daha önce yazdığımız JS [faireSimulation] işlevinin çalıştırılmasını tetikler;
- JS [faireSimulation] işlevi daha sonra, şu anda üzerinde çalıştığımız [/Pam/FaireSimulation] sunucu eylemine bir Ajax çağrısı yapar. Şu anda, JS ve [faireSimulation] işlevleri sunucu eylemine herhangi bir bilgi aktarmamaktadır. Bu işlevlerin, kullanıcı tarafından girilen değerleri sunucu eylemine iletmesi gerekecektir;
- [/Pam/FaireSimulation] sunucu eylemi, JS ve [faireSimulation] işlevleri tarafından gönderilen veriler içinden girilen değerleri alacaktır.
2. maddeden başlayalım: JS ve [faireSimulation] fonksiyonları, kullanıcı tarafından girilen değerleri [/Pam/FaireSimulation] sunucu eylemine göndermelidir.
Görev: JS [faireSimulation] işlevini, kullanıcı tarafından girilen değerleri gönderecek şekilde tamamlayın. Bu sorunun ele alındığı 7.6.5 numaralı paragrafındaki örnekten yararlanabilirsiniz.
Şimdi yukarıdaki 3. maddeyi ele alalım. [/Pam/FaireSimulation] sunucu eylemi, JS ve [faireSimulation] işlevleri tarafından gönderilen değerleri almalıdır.
Görev: [FaireSimulation] sunucu yöntemini, JS ve [faireSimulation] işlevleri tarafından gönderilen değerleri kullanarak maaşı hesaplayacak şekilde tamamlayın. Bu sorunun ele alındığı 7.6.5 numaralı paragrafındaki örnekten yine yararlanabiliriz. Şu an için, gönderilen değerlerden elde edilen modelin hala geçerli olduğunu varsayacağız.
İpucu: [FaireSimulation] sunucu eylemi şu şekilde ilerler:
// simülasyon yap
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application, FormCollection data)
{
// eylem modelinin oluşturulması
...
// bu modele kaydedilen değerleri almaya çalışıyoruz
...
// maaş hesaplanıyor
FeuilleSalaire feuilleSalaire = ...
// maaş bordrosu görüntüleniyor
return PartialView("Simulation", feuilleSalaire);
}
İşte bir yürütme örneği:
![]() |
[Justine Laverti] seçilir. Bunun üzerine şu sonuç elde edilir:
![]() |
[Justine Laverti]'e ait hayali maaş bordrosunu elde ettik. Daha önce hesaplanan tek maaş bordrosu [Marie Jouveinal]'e aitti. Dolayısıyla, çalışanın seçimi için girilen değer kullanıldı. Saat sayısı ve gün sayısı konusunda bir şey söylenemez, çünkü simüle ettiğimiz [métier] katmanı bunları dikkate almıyor.
9.12.4. Hata Yönetimi
Şu örneği inceleyelim:
![]() |
- [1]'te, var olmayan bir çalışan seçilir (9.7.5. paragrafında simüle edilen [métier] katmanının tanımına bakınız;
- [2]'te simülasyon yapılır;
- aşağıdaki [3]'te bir hata sayfası görüntülenir.
![]() |
Ne oldu?
JS [faireSimulation] işlevi çalıştırıldı. Kodu şuna benziyor:
function faireSimulation() {
...
// manuel olarak Ajax çağrısı yapılıyor
$.ajax({
url: '/Pam/FaireSimulation',
...
beforeSend: function () {
// bekleme sinyali yanıyor
loading.show();
},
success: function (data) {
...
},
error: function (jqXHR) {
// hata mesajı görüntülenir
simulation.html(jqXHR.responseText);
simulation.show();
},
complete: function () {
// bekleme sinyali kapalı
loading.hide();
}
});
// menü
setMenu([lnkEffacerSimulation, lnkEnregistrerSimulation, lnkTerminerSession, lnkVoirSimulations]);
}
Ajax çağrısı başarısız oldu ve 14-18. satırlardaki işlev çalıştırıldı. Sunucu tarafından döndürülen [jqXHR.responseText] hata sayfası görüntülendi. Bu sayfa oldukça ayrıntılıdır. Simüle edilen [métier] katmanı, kendisine sağlanan SS numarasının mevcut bir çalışana ait olmaması nedeniyle bir istisna oluşturdu (bkz. 9.7.5. paragrafında simüle edilen [métier] katmanının kodu). Bu durumu düzgün bir şekilde yönetmemiz gerekiyor.
Sunucu tarafında bir hata tespit edildiğinde, JS müşterisine geri gönderilecek olan [Erreurs.chtml] adlı bir kısmi görünüm oluşturacağız:
![]() |
[Erreurs.chtml] kısmi görünümünün kodu şöyledir:
@model IEnumerable<string>
<hr />
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
@foreach (string msg in Model)
{
<li>@msg</li>
}
</ul>
- 1. satır: Görünüm, şablon olarak bir hata mesajı listesi alır;
- 5-10. satırlar: bu mesajlar HTML listesinde görüntülenir;
Şimdi, [FaireSimulation] sunucu eyleminin kodunu şu şekilde değiştirelim:
// simülasyon yap
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application, FormCollection data)
{
...
// maaş hesaplanıyor
FeuilleSalaire feuilleSalaire = null;
Exception exception=null;
try
{
// maaş hesaplama
feuilleSalaire = ...
}
catch (Exception ex)
{
exception = ex;
}
// hata mı?
if (exception == null)
{
// maaş bordrosu görüntüleniyor
return PartialView("Simulation", feuilleSalaire);
}
else
{
// hata sayfası görüntüleniyor
return PartialView("Erreurs", Static.GetErreursForException(exception));
}
}
- 9-17. satırlar: Maaş hesaplaması artık bir try / catch bloğu içinde yapılmaktadır;
- 27. satır: Hata oluşması durumunda, statik [Static.GetErreursForException(exception)] yöntemi tarafından sağlanan hata mesajları listesini şablon olarak kullanarak [Erreurs.cshtml] kısmi görünümü görüntülenir.
[Static] sınıfında iki statik yardımcı işlev [1] bir araya getirilmiştir:
![]() |
using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace PamWeb.Infrastructure
{
public class Static
{
// bir istisna ile ilgili hata mesajlarının listesi
public static List<string> GetErreursForException(Exception ex)
{
List<string> erreurs = new List<string>();
while (ex != null)
{
erreurs.Add(ex.Message);
ex = ex.InnerException;
}
return erreurs;
}
// geçersiz bir şablonla ilgili hata mesajlarının listesi
public static List<string> GetErreursForModel(ModelStateDictionary état)
{
List<string> erreurs = new List<string>();
if (!état.IsValid)
{
foreach (ModelState modelState in état.Values)
{
foreach (ModelError error in modelState.Errors)
{
erreurs.Add(getErrorMessageFor(error));
}
}
}
return erreurs;
}
// eylem şablonundaki bir öğeyle ilgili hata mesajı
static private string getErrorMessageFor(ModelError error)
{
if (error.ErrorMessage != null && error.ErrorMessage.Trim() != string.Empty)
{
return error.ErrorMessage;
}
if (error.Exception != null && error.Exception.InnerException == null && error.Exception.Message != string.Empty)
{
return error.Exception.Message;
}
if (error.Exception != null && error.Exception.InnerException != null && error.Exception.InnerException.Message != string.Empty)
{
return error.Exception.InnerException.Message;
}
return string.Empty;
}
}
}
- 10-19. satırlar: [GetErreursForException] statik işlevi, bir istisna yığınının hata listesini döndürür;
- 22-36. satırlar: [GetErreursForModel] statik fonksiyonu, geçersiz bir eylem şablonundaki hata listesini döndürür. Bu fonksiyonun kodu ile [getErrorMessageFor] özel yönteminin kodu (39-54. satırlar) daha önce ele alınmıştı.
Bunu yaptıktan sonra, hata durumunu yeniden test edebiliriz:
![]() |
- [1]'te, mevcut olmayan çalışanı seçiyoruz;
- [2]'te simülasyonu gerçekleştiriyoruz;
- [3]'te yeni hata sayfasını alıyoruz.
[FaireSimulation] sunucu eylemine geri dönelim:
// simülasyon yapma
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application, FormCollection data)
{
// eylem modelinin oluşturulması
IndexModel modèle = new IndexModel() { Application = application};
// modele gönderilen değerleri almaya çalışılıyor
TryUpdateModel(modèle, data);
// maaş hesaplanıyor
...
}
8. satırda, 6. satırdaki modeli Ajax çağrısı ile gönderilen değerlerle güncelliyoruz. Modelin geçerliliğini kontrol etmiyoruz. Bunu yapmak gerekir, çünkü gönderilen değerlerin nereden geldiğini bilemeyiz. Birisi POST'i kurcalayıp bize geçersiz veriler göndermiş olabilir.
Görev: İstisna durumu için geliştirdiğimiz modeli takip ederek, gönderilen veriler geçersiz olduğunda bir hata sayfası göndermek üzere [FaireSimulation] sunucu eylemini değiştirin. Bunun için, [Static] sınıfının [GetErreursForModel] statik yöntemini kullanacağız.
Bu değişikliği nasıl test edebiliriz? 9.11.3 numaralı paragrafta, girilen değerler geçersiz olduğunda JS ve [faireSimulation] işlevlerinin POST işlemini gerçekleştirmesini engellediniz. Bunu gerçekleştiren satırları yorum satırına alın ve ardından aşağıdaki testi yapın:
![]() |
- [1]'te, geçersiz değerlerle simülasyon yapıyoruz;
- [2]'te, az önce oluşturduğumuz hata sayfasını doğru bir şekilde alıyoruz; bu da sunucu tarafındaki doğrulayıcıların düzgün çalıştığının kanıtıdır.
Devamında, JS ve [faireSimulation] işlevlerinde az önce yorum satırına aldığınız satırların yorum satırını kaldırmayı unutmayın.
9.13. 7. Adım: Kullanıcı oturumu oluşturma
[Simulateur de calcul de paie] uygulaması, kullanıcının [Faire la simulation] bağlantısı ile çeşitli bordro simülasyonları yapmasına, [Enregistrer la simulation] bağlantısı ile bunları kaydetmesine, [Voir les simulations] bağlantısıyla görüntüleyebilir ve [Retirer la simulation] bağlantısıyla silebilir. Kullanıcının ardışık iki isteği arasında, oturum mekanizmasıyla bir bellek oluşturulmadıkça (bkz. paragraf 4.10) herhangi bir bellek bulunmadığını biliyoruz. Burada, kullanıcının zaman içinde kaydettiği simülasyonların listesini oturumda tutmamız gerektiği oldukça açıktır. Hatırlanması gereken başka veriler de vardır: kullanıcı bir simülasyon yaptığında, bu simülasyon ancak kullanıcı [Enregistrer la simulation] bağlantısı ile talep ederse simülasyonlar listesine kaydedilir. Kullanıcı bunu yaptığında, bir önceki istekte hesaplanan simülasyonu bulabilmemiz gerekir. Bunun için söz konusu simülasyon da oturuma kaydedilecektir. Son olarak, simülasyonları 1'den başlayarak numaralandıracağız. Yeni bir simülasyonu doğru şekilde numaralandırmak için, yine oturumda saklanan bir önceki simülasyonun numarasını bilmemiz gerekir.
4.10 numaralı paragrafta, bir eylemin oturuma erişebilmesi için eylem giriş parametresi olarak oturum modeli kavramını tanıtmıştık. Bu kavramı tekrar ele alacağız. Bu kavram size belirsiz geliyorsa, ilgili paragrafı tekrar okumanızı öneririz.
Aşağıdaki [SessionModel] sınıfını oluşturuyoruz:
![]() |
Kodu şu şekildedir:
using Pam.Web.Models;
using System.Collections.Generic;
namespace Pam.Web.Models
{
public class SessionModel
{
// simülasyon listesi
public List<Simulation> Simulations { get; set; }
// bir sonraki simülasyon numarası
public int NumNextSimulation { get; set; }
// son simülasyon
public Simulation Simulation { get; set; }
// oluşturucu
public SessionModel()
{
// simülasyon listesi boş
Simulations = new List<Simulation>();
// bir sonraki simülasyon numarası
NumNextSimulation = 1;
}
}
}
9. ve 13. satırlardaki [Simulation] sınıfı, bir simülasyonla ilgili bilgileri kaydedecektir. Neleri kaydetmemiz gerekiyor? [Faire la simulation] bağlantısı, [FeuilleSalaire] türünde bir maaş bordrosu hesaplar. Bunu simülasyona eklemek doğal görünüyor. Ayrıca, bu maaş bordrosuna yol açan bilgileri de kaydetmemiz gerekiyor:
- seçilen çalışan. Bu bilgi, [FeuilleSalaire.Employe] alanında bulunur. Dolayısıyla bunu ikinci kez kaydetmeye gerek yoktur;
- çalışılan saat ve gün sayısı. Bu bilgiler [FeuilleSalaire] türünde yer almamaktadır. Dolayısıyla bunları kaydetmemiz gerekir.
Son olarak, her simülasyon bir numara ile tanımlanır. Dolayısıyla şu [Simulation] sınıfını kullanabiliriz:
using Pam.Metier.Entites;
namespace Pam.Web.Models
{
public class Simulation
{
// simülasyon numarası
public int Num { get; set; }
// çalışılan saat sayısı
public double HeuresTravaillées { get; set; }
// çalışılan gün sayısı
public int JoursTravaillés { get; set; }
// maaş bordrosu
public FeuilleSalaire FeuilleSalaire { get; set; }
}
}
[FaireSimulation] sunucu eylemi, maaş bordrosunu hesaplamanın yanı sıra bir simülasyon oluşturmalı ve bunu oturuma eklemelidir. Bunun için, oturum şablonunu parametre olarak alacaktır:
// simülasyon yapma
[HttpPost]
public PartialViewResult FaireSimulation(ApplicationModel application, SessionModel session, FormCollection data)
{
// eylem şablonunun oluşturulması
IndexModel modèle = new IndexModel() { Application = application };
// şablona girilen değerleri almaya çalışıyoruz
TryUpdateModel(modèle, data);
// şablon geçerli mi?
if (!ModelState.IsValid)
{
// hata sayfası görüntüleniyor
return PartialView("Erreurs", Static.GetErreursForModel(ModelState));
}
// maaş hesaplanıyor
FeuilleSalaire feuilleSalaire = null;
Exception exception = null;
try
{
// maaş hesaplama
feuilleSalaire = application.PamMetier.GetSalaire(modèle.SS, modèle.HeuresTravaillées, (int)modèle.JoursTravaillés);
}
catch (Exception ex)
{
exception = ex;
}
// hata mı?
if (exception != null)
{
// hata sayfası görüntüleniyor
return PartialView("Erreurs", Static.GetErreursForException(exception));
}
// bir simülasyon oluşturulur ve oturuma eklenir
session.Simulation = ...
// maaş bordrosu görüntüleniyor
return PartialView("Simulation", feuilleSalaire);
}
- 3. satır: eylem, parametre olarak oturum modelini alır;
Ödev 1: İşlemin kodunu 34. satırda tamamlayın
Görev 2: 4.10 numaralı paragrafta belirtilen adımları izleyerek, eylemin [SessionModel session] parametresinin çerçeve tarafından doğru şekilde başlatılması için gerekli işlemleri yapın. Herhangi bir işlem yapılmazsa, bu parametre için null işaretçisi elde edilecektir.
9.14. Adım 8: Simülasyonu kaydetme
9.14.1. Sorun
Bir simülasyon gerçekleştirdiğimizde, bunu kaydedebiliriz:
![]() |

[Simulations.cshtml] kısmi görünümü artık kullanıcı tarafından yapılan simülasyonların listesini göstermektedir. Hesaplanan maaş bordrosunun hayali olduğunu hatırlatırız.
9.14.2. [EnregistrerSimulation] sunucu eyleminin yazılması
Ajax bağlantısı [Enregistrer la simulation], şu ana kadar aşağıdaki gibi olan [EnregistrerSimulation] sunucu eylemini çağırır:
[HttpPost]
public PartialViewResult EnregistrerSimulation()
{
return PartialView("Simulations");
}
Şu şekilde değiştirilmiştir:
// bir simülasyon kaydediliyor
[HttpPost]
public PartialViewResult EnregistrerSimulation(SessionModel session)
{
// oturumun simülasyon listesine en son yapılan simülasyon kaydedilir
...
// oturumda bir sonraki simülasyon numarasını artırır
...
// simülasyon listesi görüntülenir
...
}
- 1. satır: [EnregistrerSimulation] eyleminin oturuma erişimi olması gerekir. Bu nedenle, parametresi olarak oturum şablonu kullanılır.
Görev: [EnregistrerSimulation] sunucu eylemini tamamlayın.
9.14.3. [Simulations.cshtml] kısmi görünümünün yazılması
Önceki eylem olan [EnregistrerSimulation], kullanıcı tarafından yapılan simülasyonların listesini şablon olarak kullanarak [Simulations.cshtml] kısmi görünümünü görüntüler. Kod şu şekildedir:
@model IEnumerable<Simulation>
@using Pam.Web.Models
@if (Model.Count() == 0)
{
<h2>Votre liste de simulations est vide</h2>
}
@if (Model.Count() != 0)
{
<h2>Liste des simulations</h2>
...
}
Ödev 1: [Simulations.cshtml] kısmi görünümünün kodunu tamamlayın. Simülasyonların görüntülenmesi için HTML tablosu kullanılacaktır. 5.4. paragrafındaki örneklerden yararlanabilirsiniz.
Not: HTML tablosundaki her simülasyonun [retirer] bağlantısı, aşağıdaki biçimdeki bir JavaScript bağlantısı olacaktır:
burada N, simülasyonun numarasıdır.
Ödev 2: Simülasyonlar yaparak uygulamanızı test edin. Simülasyonları yapmak için aşağıdaki adımları tekrar tekrar uygulayın: 1) [F5] ile uygulama sayfasını yükleyin, 2) bir simülasyon yapın, 3) simülasyonu kaydedin. Simülasyonlar oturumda birikecek ve bu durum [Simulations.cshtml] görünümünde yansıtılmalıdır.
Görev 3: [Simulations.cshtml] kısmi görünümünü, HTML tablosundaki satırların renklerinin sırayla değişecek şekilde iyileştirin.

HTML tablosundaki <tr> satırlarına, [/Content/Site.css] stil sayfasında tanımlanan CSS, [pair] ve [impair] sınıfları sırayla atanacaktır:
.impair {
background-color: beige;
}
.pair {
background-color: lightsteelblue;
}
9.15. 9. Adım: Giriş formuna geri dönün
9.15.1. Sorun
Simülasyon listesini aldıktan sonra, bir süredir yapamadığımız giriş formuna geri dönebiliriz:


9.15.2. [Formulaire] sunucu eyleminin yazılması
[Retour au formulaire de simulation] adlı Ajax bağlantısı, [Formulaire] adlı sunucu eylemini çağırır; bu eylemin kodu şimdiye kadar şöyleydi:
[HttpPost]
public PartialViewResult Formulaire()
{
return PartialView("Formulaire");
}
Bu eylemin görüntülediği [Formulaire] kısmi görünümü, [IndexModel] şablonunu beklemektedir (aşağıdaki 1. satır):
@model Pam.Web.Models.IndexModel
@using (Html.BeginForm("FaireSimulation", "Pam", FormMethod.Post, new { id = "formulaire" }))
{
...
}
<div id="simulation" />
Bu nedenle [Retour au formulaire de simulation] bağlantısı artık çalışmıyordu.
Yapılacak iş: [Formulaire] sunucu eyleminin yeni sürümünü yazmak (yeniden yazılması gereken 2 satır) ve ardından testleri yapmak.
9.15.3. [retourFormulaire] JavaScript işlevinin değiştirilmesi
Daha önce yapılan değişiklikle artık forma geri dönülebiliyor, ancak bu durumda bir hata ortaya çıkıyor:
![]() |
- [1]'te, giriş formuna geri dönülür;
- [2]'te, hatalı girdilerle bir simülasyon yapıyoruz. Bu durumda, istemci tarafındaki doğrulayıcıların artık çalışmadığını görüyoruz. Burada, 9.12.4. paragrafında yapılan çalışma sayesinde sunucu çağrıldı ve bir hata sayfası geri gönderildi.
Bu hata, 7.6.7. paragrafında tespit edilmiş ve giderilmiştir.
Ödev: 7.6.7 paragrafındaki adımları izleyerek, [retourFormulaire] adlı JavaScript işlevini düzeltin ve ardından istemci tarafındaki doğrulayıcıların yeniden çalıştığını kontrol etmek için testler yapın.
9.16. Adım 10: Simülasyon listesine bakın
9.16.1. Sorun
Simülasyon formuyla çalışırken, yaptığınız simülasyonların listesini görüntüleyebilirsiniz:
![]() | ![]() |
9.16.2. [VoirSimulations] sunucu eyleminin yazılması
[Voir les simulations] Ajax bağlantısı, [VoirSimulations] sunucu eylemini çağırır; bu eylemin kodu şimdiye kadar şöyleydi:
// simülasyonları görüntüle
[HttpPost]
public PartialViewResult VoirSimulations()
{
return PartialView("Simulations");
}
Görüntülediği [Simulations] kısmi görünümü, [IEnumerable<Simulation>] şablonunu beklemektedir (aşağıdaki 1. satır):
@model IEnumerable<Simulation>
@using Pam.Web.Models
@if (Model.Count() == 0)
{
<h2>Votre liste de simulations est vide</h2>
}
@if (Model.Count() != 0)
{
<h2>Liste des simulations</h2>
...
}
Bu nedenle [Voir les simulations] bağlantısı artık çalışmıyordu.
Yapılacak iş: [VoirSimulations] sunucu eyleminin yeni sürümünü yazmak (yeniden yazılması gereken 2 satır) ve ardından testleri yapmak.
9.17. Adım 11: Oturumu sonlandırma
9.17.1. Sorun
[Ajax] ve [Terminer la session] bağlantıları kullanılarak kullanıcının oturumu her an sonlandırılabilir. Bu işlem, mevcut oturumu sonlandırıp yeni bir oturum başlatır. Ayrıca, form görünümüne geri dönülür:
![]() |
![]() |
- [1]'te iki simülasyon yaptık ve oturumu sonlandırdık;
- [2]'te, giriş formuna geri dönülür. Simülasyonları görmek istiyoruz;
- [3]'te, oturum değişikliği nedeniyle simülasyon listesi artık boştur.
9.17.2. [TerminerSession] sunucu eyleminin yazılması
Ajax bağlantısı [Terminer la session], şu ana kadar kodu aşağıdaki gibi olan [TerminerSession] sunucu eylemini çağırır:
// oturumu sonlandır
[HttpPost]
public PartialViewResult TerminerSession()
{
return PartialView("Formulaire");
}
Görüntülediği [Formulaire] kısmi görünümü, [IndexModel] şablonunu bekler (aşağıdaki 1. satır):
@model Pam.Web.Models.IndexModel
@using (Html.BeginForm("FaireSimulation", "Pam", FormMethod.Post, new { id = "formulaire" }))
{
...
}
<div id="simulation" />
Bu nedenle [Terminer la session] bağlantısı artık çalışmıyordu.
Yapılacak iş: [TerminerSession] sunucu eyleminin yeni sürümünü yazmak (yeniden yazılması gereken 2 satır) ve ardından testleri yapmak.
Not: Eylem içinde oturumu sonlandırmak için şunu yazın:
9.17.3. [terminerSession] JavaScript işlevinde yapılan değişiklik
Daha önce yapılan değişiklikle artık forma geri dönülebiliyor, ancak bu durumda 9.15.3 numaralı paragrafta daha önce açıklanan bir hata ortaya çıkıyor.
Ödev: 9.15.3 numaralı paragrafta izlediğiniz adımları takip ederek, [terminerSession] adlı JavaScript işlevini düzeltin ve ardından istemci tarafındaki doğrulayıcıların yeniden çalıştığını kontrol etmek için testler yapın.
9.18. Adım 12: Simülasyonu silme
9.18.1. Sorun
Bir simülasyon yapıldığında, bunu [Effacer la simulation] JavaScript bağlantısı ile silebilirsiniz:
![]() | ![]() |
9.18.2. [effacerSimulation] müşteri eyleminin yazılması
[effacerSimulation] JavaScript işlevinin şu anki kodu şöyledir:
function effacerSimulation() {
// formdaki girdiler silinir
// ...
// varsa simülasyon gizlenir
$("#simulation").hide();
// menü
setMenu([lnkFaireSimulation, lnkTerminerSession, lnkVoirSimulations]);
}
Görev: Bu kodu tamamlayın. 7.6.6 numaralı paragrafta verilen örnekten yararlanabilirsiniz.
9.19. Adım 13: Bir simülasyonu kaldırma
9.19.1. Sorun
Simülasyonlar sayfasında, [retirer] adlı JavaScript bağlantısını kullanarak bazı simülasyonları silebilirsiniz:


9.19.2. [retirerSimulation] müşteri eyleminin yazılması
[retirer] bağlantıları şu HTML biçimine sahiptir:
burada N, simülasyon numarasıdır.
Görev: 9.9.3 numaralı paragraflarda anlatılan adımları izleyerek JS ve [retirerSimulation] işlevlerini yazınız. Bu fonksiyon, POST türünde bir Ajax çağrısı yaparak [/Pam/RetirerSimulation] eylemine ulaşacaktır. N verisini num=N biçiminde gönderecektir.
Not: JS [retirerSimulation] işlevi, daha önce yazdığınız ve sunucuya Ajax çağrısı yapan diğer JS işlevlerine benzerdir. Buradaki tek yenilik, bir formda bulunmayan bir değerin POST ile gönderilmesidir. Gönderilen değerlerin şu biçimde bir karakter dizisi içinde birleştirildiği bilinmektedir:
JS [retirerSimulation] işlevi bu nedenle şu şekilde olacaktır:
function retirerSimulation(N) {
// manuel olarak Ajax çağrısı yap
$.ajax({
url: '/Pam/RetirerSimulation',
...
data:"num="+N,
...
});
// menü
setMenu([lnkRetourFormulaire, lnkTerminerSession]);
}
- 6. satır: [data] özelliğinin bir Ajax çağrısı olan JQuery, sunucuya gönderilen dizeyi temsil eder.
9.19.3. [RetirerSimulation] sunucu eyleminin yazılması
[RetirerSimulation] sunucu eylemi:
- [num] adlı, bir simülasyon numarası olan POST parametresini alır;
- oturumda kaydedilen simülasyonlar listesinden bu numaraya sahip simülasyonu kaldırmalıdır;
- ardından yeni simülasyon listesini görüntülemelidir.
Görev: [RetirerSimulation] sunucu eylemini yazın. [num] adlı POST parametresini nasıl alacağınızı öğrenmek için 4.1 numaralı paragrafa bakın.
9.20. Adım 14: Uygulamanın başlatma yönteminin iyileştirilmesi
Web uygulamamız tamamlandı. Simüle edilmiş bir [métier] sınıfıyla çalışır durumda. Geliştirdiğimiz mimariyi hatırlayalım:
![]() |
[métier] katmanının gerçek uygulamasına geçmeden önce halledilmesi gereken birkaç ayrıntı var ve bunlar uygulamanın başlatma yönteminde yer alıyor: [Global.asax] içindeki [Application_Start] yöntemi:
![]() |
[Global.asax] içindeki [Application_Start] yöntemi, uygulamanın başlatılması sırasında yalnızca bir kez yürütülür. İşte burada [Web.config] yapılandırma dosyası kullanılabilir. Şu anda, [Application_Start] yöntemimiz şu şekilde görünüyor:
// uygulama
protected void Application_Start()
{
// ----------Otomatik olarak oluşturuldu
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// -------------------------------------------------------------------
// ---------- özel yapılandırma
// -------------------------------------------------------------------
// uygulama kapsamı verileri
ApplicationModel application = new ApplicationModel();
Application["data"] = application;
// katman örneği [métier]
application.PamMetier = new PamMetier();
...
// model bağlayıcıları
...
}
- satırda, iş katmanı bir new operatörüyle örneklenir. Ayrıca, uygulama modeli şu şekilde tanımlanmıştır:
public class ApplicationModel
{
// --- uygulama kapsamı verileri ---
public Employe[] Employes { get; set; }
public IPamMetier PamMetier { get; set; }
public SelectListItem[] EmployesItems { get; set; }
}
Yukarıdaki 5. satırda, [PamMetier] özelliğinin türünün [IPamMetier] arayüzünün türü olduğu görülmektedir. Bu, söz konusu özelliğin bu arayüzü uygulayan herhangi bir nesne tarafından başlatılabileceği anlamına gelir. Ancak [Application_Start]'in 17. satırında, [IPamMetier]'i uygulayan bir sınıfın adını sabit olarak yazdık. Dolayısıyla, [métier] katmanı, [IPamMetier]'i uygulayan yeni bir sınıfla uygulanacak olursa, bu satırı değiştirmek gerekir. Bu çok önemli bir sorun değildir, ancak önlenebilir. [IPamMetier] arayüzünün uygulama sınıfının tanımı bir yapılandırma dosyasına taşınabilir. Uygulamayı değiştirmek için bu yapılandırma dosyasının içeriği değiştirilir. .NET kodunda herhangi bir değişiklik yapılması gerekmez.
Burada [Spring.net] bağımlılık enjeksiyon konteynerini kullanacağız. Aynı işi, belki daha iyi ve daha basit bir şekilde yapan başka .NET çerçeveleri de mevcuttur.
Projenin mimarisi şu şekilde gelişmektedir:
![]() |
- [A]'te, [ASP.NET MVC] katmanının başlatma yöntemi, [Spring.net]'ten simüle edilmiş [métier] katmanına ilişkin bir referans isteyecektir;
- [B]'te, [Spring.net], hangi sınıfı örneklendirmesi gerektiğini belirlemek için yapılandırma dosyasını kullanarak simüle edilmiş [métier] katmanını oluşturacaktır;
- [C]'te, [Spring.net], simüle edilmiş [métier] katmanının referansını [ASP.NET MVC] katmanına devredecektir.
Varsayılan olarak, [Spring.net] tarafından yönetilen nesnelerin tekil nesneler (singleton) olduğu unutulmamalıdır: bu nesnelerin yalnızca tek bir örneği mevcuttur. Dolayısıyla, örneğimizde daha sonra bir kod parçası [Spring.net]'ten simüle edilmiş [métier] katmanına ait bir referans talep ederse, [Spring.net] sadece başlangıçta oluşturulan nesneye ait referansı döndürür.
9.20.1. Web projesine [Spring] referanslarının eklenmesi
[Spring.net]'i kullanacağız. Bu çerçeve, DLL adıyla gelir ve projenin referanslarına eklenmesi gerekir. Şu şekilde ilerleyebiliriz:
![]() |
[1]'te, projenin [References] dalına sağ tıklayın ve ardından [Gérer les packages NuGet] seçeneğini seçin. İnternet bağlantısı gereklidir. Ardından, JQuery ve [Globalize] kütüphaneleri için daha önce yapıldığı gibi işlem yapın. [Spring.core] anahtar kelimesini arayın ve bu paketi yükleyin. Kurulum sonucunda iki DLL paketi eklenir: [Spring.core] [2] ve [Common.Logging] [3]. Aşağıdaki örneklerde Spring'in 1.3.2 sürümü kullanılmıştır.
Not: İnternet bağlantınız yoksa, bu DLL dosyalarını bu vaka çalışmasının destek materyallerinde bulunan [lib] klasöründe bulabilirsiniz.
9.20.2. [web.config]'in yapılandırılması
[IPamMetier] arayüzünün uygulama sınıfı, [web.config] dosyasında tanımlanmıştır.
<configuration>
<configSections>
...
<sectionGroup name="spring">
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
</configSections>
<!-- Spring yapılandırması -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier-simule"/>
</objects>
</spring>
...
- 2-8. satırlar: Dosyadaki <configSections> etiketini bulun ve içine 4-7. satırları ekleyin;
- 4. satır: [name="spring"] özniteliği, 10-17. satırlardaki [spring] bölümüyle ilgili bilgileri gösterir;
- satır 5: DLL ve [Spring.Core] içinde yer alan [Spring.Context.Support.DefaultSectionHandler] sınıfını, 14-16. satırlardaki [objects] bölümünü işleyebilen sınıf olarak tanımlar;
- 6. satır: DLL ve [Spring.Core] içinde yer alan [Spring.Context.Support.ContextHandler] sınıfını, 11-13. satırlardaki [context] bölümünü işleyebilen sınıf olarak tanımlar;
- satır 11-13: Bu bölüm, Spring nesnelerinin yapılandırma dosyasında [/spring/objects] bölümünde, yani satır 14-16'da bulunduğunu belirten [<resource uri="config://spring/objects" />] bilgisini sağlar;
- 14-16. satırlar: [objects] etiketi, Spring nesnelerini tanıtır;
- 15. satır: [id="pammetier"] ile tanımlanan bir nesneyi tanımlar; bu nesne, DLL [pam-metier-simule] içinde bulunan [Pam.Metier.Service.PamMetier] sınıfının bir örneğidir. Burada hata yapmamak gerekir. [id] özniteliği için istediğiniz değeri girebilirsiniz. Bu tanımlayıcıyı [Global.asax] içinde kullanacaksınız. [Pam.Metier.Service.PamMetier] sınıfı, simüle ettiğimiz [métier] katmanına aittir. Tam adını öğrenmek için tanımına geri dönmeniz gerekir:
namespace Pam.Metier.Service
{
public class PamMetier : IPamMetier
{
...
DLL ve [pam-metier-simule] için, C# projesi [pam-metier-simule]'in özelliklerine bakılmalıdır:
![]() |
[1]'te belirtilen adı kullanmanız gerekir.
9.20.3. [Application_Start]'te yapılan değişiklik
[Application_Start] yöntemi şu şekilde güncellenmiştir:
using Spring.Context.Support;
// uygulama
protected void Application_Start()
{
// ----------Otomatik olarak oluşturuldu
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// -------------------------------------------------------------------
// ---------- özel yapılandırma
// -------------------------------------------------------------------
// uygulama kapsamı verileri
ApplicationModel application = new ApplicationModel();
Application["data"] = application;
// katman örneklendirme [métier]
application.PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
...
// model bağlayıcıları
...
}
- 19. satır: [ContextRegistry] dosyasını işleyebilen bir sınıf olan Spring sınıfı [ContextRegistry] kullanılır. Bunun için 1. satırdaki ad alanının içe aktarılması gerekir. [GetContext] statik yöntemi, Spring nesnelerinin konumlarını belirten [context] etiketlerinin içeriğini elde etmemizi sağlar. [GetObject] statik yöntemi ise id özniteliğiyle tanımlanan belirli bir nesneyi elde etmemizi sağlar. Artık [IPamMetier] arayüzünün uygulama sınıfının adının kodda sabit olarak yazılmadığına dikkat edin. Bu ad artık [web.config] dosyasında yer almaktadır.
Tüm bu değişiklikleri yaptıktan sonra uygulamanızı test edin. Uygulama çalışmalıdır.
9.20.4. Uygulamanın başlatılmasında oluşan bir hatayı yönetme
[Application_Start] yönteminde şunu yazmıştık:
application.PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
= işaretinin sağındaki komut başarısız olabilir. Bunun çeşitli nedenleri vardır:
- en bariz olanı, örneklenecek nesnenin adında hata yapılmasıdır;
- diğeri ise [métier] katmanının oluşturulmasında bir sorun yaşanmasıdır. Simüle edilmiş [métier] katmanımız için bu durum söz konusu olamaz, ancak bir veritabanına bağlanacak olan gerçek [métier] katmanımız için söz konusu olabilir. SGBD katmanı başlatılmayabilir, yönetilecek veritabanına ilişkin bilgiler yanlış olabilir vb.
Olası bir istisnayı bir try / catch bloğu içinde işleyeceğiz. Kod şu şekilde değişir:
// uygulama
protected void Application_Start()
{
// ----------Otomatik olarak oluşturuldu
...
// -------------------------------------------------------------------
// ---------- özel yapılandırma
// -------------------------------------------------------------------
// uygulama kapsamı verileri
ApplicationModel application = new ApplicationModel();
Application["data"] = application;
application.InitException = null;
try
{
// [métier] katmanının örneklenmesi
application.PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
catch (Exception ex)
{
application.InitException = ex;
}
//hata yoksa
if (application.InitException == null)
{
....
}
// model bağlayıcıları
...
}
- 12. satırda, uygulama modeline [InitException] adlı yeni bir özellik ekliyoruz:
public class ApplicationModel
{
// --- uygulama kapsamı verileri ---
public Employe[] Employes { get; set; }
public IPamMetier PamMetier { get; set; }
public SelectListItem[] EmployesItems { get; set; }
public Exception InitException { get; set; }
}
- yukarıdaki 7. satırda, uygulamanın başlatılması sırasında meydana gelebilecek istisna;
- [Application_Start]'in 13-21. satırları: [métier] katmanının örneklenmesi artık bir try / catch içinde gerçekleştirilir;
- 20. satır: istisna kaydedilir;
- 23-26. satırlar: Hata oluşmamışsa, daha önce yazdığımız kod çalıştırılır;
- 28. satır: Hata olsun ya da olmasın, [ModelBinders] nesneleri oluşturulur. Bu önemlidir. [ApplicationModel] uygulama modelinin çerçeve tarafından doğru şekilde bağlanacağından emin olmak istiyoruz.
Uygulamanın başlatılması sırasında [Index] sunucu eyleminin yürütüldüğünü biliyoruz. Şu an için bu eylem şöyledir:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
return View(new IndexModel() { Application = application });
}
- satırda, [Index] eylemi uygulama modelini alır. Böylece, başlatma işleminin başarılı olup olmadığını anlayabilir ve başlatma işlemi herhangi bir şekilde başarısız olursa bir hata sayfası görüntüleyebilir. Kodu şu şekilde değiştiriyoruz:
[HttpGet]
public ViewResult Index(ApplicationModel application)
{
// başlatma hatası mı?
if (application.InitException != null)
{
// menüsüz hata sayfası
return View("InitFailed",Static.GetErreursForException(application.InitException));
}
// hata yok
return View(new IndexModel() { Application = application });
}
- satırda, başlatma hatası durumunda, başlatma sırasında meydana gelen istisnanın hata mesajları listesini şablon olarak kullanarak [InitFailed.cshtml] görünümünü görüntülüyoruz. [Static.GetErreursForException] yöntemi, 9.12.4. paragrafında tanıtılmış ve açıklanmıştır. [InitFailed.cshtml] görünümü şu şekilde olacaktır:
![]() |
Kodu şu şekildedir:
@model IEnumerable<string>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="~/Content/Site.css" />
</head>
<body>
<table>
<tbody>
<tr>
<td>
<h2>Simulateur de calcul de paie</h2>
</td>
</tbody>
</table>
<hr />
<h2>Les erreurs suivantes se sont produites à l'initialisation de l'application : </h2>
<ul>
@foreach (string msg in Model)
{
<li>@msg</li>
}
</ul>
</body>
</html>
- 1. satır: Görünüm şablonu, hata mesajlarından oluşan bir listedir. Bu mesajlar, 24-29. satırlarda yer alan HTML listesinde görüntülenir;
- 3. satır: Bu görünüm, [_Layout.cshtml] ana sayfasını kullanmaz. Zira bu belgeyle gelen menü istenmemektedir. Bu nedenle, 5-23. satırlarda tam bir HTML sayfası oluşturulur.
Test etmek için, [Application_Start] dosyasında [métier] katmanının örneklenmesini şu şekilde değiştirmeniz yeterlidir:
try
{
// [métier] katmanının örneklenmesi
application.PamMetier = ContextRegistry.GetContext().GetObject("xx") as IPamMetier;
}
catch (Exception ex)
{
application.InitException = ex;
}
- satırda, Spring nesneleri arasında bulunmayan bir nesne aranıyor.
Bu değişiklikleri kaydedip uygulamayı başlattığınızda, aşağıdaki sayfa görüntülenir:
![]() |
Menü içermeyen bir hata sayfası görüntülenir. Kullanıcı, hatayı fark etmekten başka bir şey yapamaz. İstenen sonuç budur.
9.21. Şu anda neredeyiz?
Artık simüle edilmiş bir iş katmanıyla çalışan, işlevsel bir web uygulamamız var. Mimarisi şu şekildedir:
![]() |
[ASP.NET MVC] katmanı, [IPamMetier] arayüzü aracılığıyla simüle edilmiş iş katmanıyla birlikte çalışır. Bu simüle edilmiş iş katmanını, söz konusu arayüze uyan gerçek bir iş katmanıyla değiştirirsek, web katmanının kodunda herhangi bir değişiklik yapmamız gerekmeyecektir. [Spring.net] sayesinde, [web.config]'te sadece [IPamMetier] arayüzünün uygulama sınıfını değiştirmemiz yeterli olacaktır. Bu yönde ilerleyeceğiz.
Yeni mimari şu şekilde olacaktır:
![]() |
Şu unsurları sırasıyla açıklayacağız:
- [EF5] katmanı, SGBD'e bağlıdır. Bu katman, Entity Framework 5 (EF5) ile uygulanacaktır;
- [DAO] katmanı, [EF5] katmanı aracılığıyla verilere erişimi yönetir. Bu sayede SGBD'in varlığını göz ardı edebilir. Bu katman, yalnızca [Employe, Cotisations, Indemnites] uygulamasının varlıklarını işler;
- maaş hesaplamasını gerçekleştiren [métier] katmanı.
Yeni mimari, bu belgenin en başında 1.1 numaralı paragrafta sunulan ve şimdi tekrar hatırlattığımız mimaridir:
![]() |
- [Web] katmanı, web uygulamasının kullanıcısıyla etkileşimde bulunan katmandır. Kullanıcı, bir tarayıcı aracılığıyla görüntülenen web sayfaları vasıtasıyla web uygulamasıyla etkileşime girer. ASP.NET ve MVC bu katmanda yer alır ve yalnızca bu katmanda bulunur;
- [métier] katmanı ise maaş veya fatura hesaplaması gibi uygulama yönetim kurallarını uygular. Bu katman, [Web] katmanı aracılığıyla kullanıcıdan gelen verileri ve [DAO] katmanı aracılığıyla SGBD'ten gelen verileri kullanır;
- [DAO] katmanı (Veri Erişim Nesneleri), [ORM] katmanı (Nesne-İlişkisel Eşleştirici) ve ADO.NET konektörü, SGBD'teki verilere erişimi yönetir. [ORM] katmanı, [DAO] katmanı tarafından işlenen nesneler ile ilişkisel bir veritabanındaki verilerin satırları ve sütunları arasında bir köprü görevi görür. Dünya çapında yaygın olarak kullanılan iki ORM vardır: NET, NHibernate (http://sourceforge.net/projects/nhibernate/) ve Entity Framework (http://msdn.microsoft.com/en-us/data/ef.aspx);
- katmanların entegrasyonu, Spring (http://www.springframework.net/ ) gibi bir bağımlılık enjeksiyon konteyneri (Dependency Injection Container) aracılığıyla gerçekleştirilebilir;
[métier], [DAO] ve [EF5] katmanları, C# projeleri kullanılarak uygulanacaktır. Bundan sonra, Visual Studio Express 2012 for Desktop ile çalışacağız.
9.22. Adım 15: Entity Framework 5 katmanının kurulması
![]() |
[EF5] katmanının oluşturulması, kodlamadan çok yapılandırma ile ilgilidir. Bu katmanın yazımını anlamak için, URL ve [http://tahe.developpez.com/dotnet/ef5cf-02/] adreslerinde bulunan [Introduction à Entity Framework 5 Code First] belgesini okuyun. Bu oldukça kapsamlı bir belgedir. Temel bilgiler ilk dört bölümde yer almaktadır. Özellikle okunması gereken paragraflar belirtilecektir. Bu belgeye atıfta bulunurken [refEF5] notasyonunu kullanacağız.
Ayrıca, zaman zaman C# kavramlarına ihtiyacımız olacak. Bu durumda, URL [http://tahe.developpez.com/dotnet/csharp/] adresinde bulunan [Introduction au langage C#] dersine [refC#] notasyonu ile atıfta bulunacağız.
9.22.1. Veritabanı
Uygulamanın veritabanı 9.4. paragrafında tanıtılmıştır. Bu, MySQL adlı ve [dbpam_ef5] (pam=Çocuk Bakıcısı Maaşı) olarak adlandırılan bir veritabanıdır. Bu veritabanının root adlı, şifresiz bir yöneticisi vardır.
Veritabanı şemasını hatırlayalım. Üç tablo içermektedir:

EMPLOYES (INDEMNITE_ID) sütunu ile INDEMNITES (ID) sütunu arasında bir yabancı anahtar ilişkisi bulunmaktadır. Bu veritabanının yapısının bir kısmı, EF5 ile birlikte kullanılması nedeniyle belirlenmiştir.
Veritabanını oluşturan SQL komut dosyası şöyledir:
-- phpMyAdmin SQL Dökümü
-- sürüm 3.5.1
-- http://www.phpmyadmin.net
--
-- İstemci: localhost
-- Oluşturulma tarihi: 4 Kasım 2013 Pazartesi, 09:34
-- Sunucu sürümü: 5.5.24-log
-- PHP sürümü: 5.4.3
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Veritabanı: `dbpam_ef5`
--
-- --------------------------------------------------------
--
-- `cotisations` tablosunun yapısı
--
CREATE TABLE IF NOT EXISTS `cotisations` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`SECU` double NOT NULL,
`RETRAITE` double NOT NULL,
`CSGD` double NOT NULL,
`CSGRDS` double NOT NULL,
`VERSIONING` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=12 ;
--
-- `cotisations` tablosunun içeriği
--
INSERT INTO `cotisations` (`ID`, `SECU`, `RETRAITE`, `CSGD`, `CSGRDS`, `VERSIONING`) VALUES
(11, 9.39, 7.88, 6.15, 3.49, 1);
--
-- `cotisations` tetikleyicileri
--
DROP TRIGGER IF EXISTS `INCR_VERSIONING_COTISATIONS`;
DELIMITER //
CREATE TRIGGER `INCR_VERSIONING_COTISATIONS` BEFORE UPDATE ON `cotisations`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=OLD.VERSIONING+1;
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `START_VERSIONING_COTISATIONS`;
DELIMITER //
CREATE TRIGGER `START_VERSIONING_COTISATIONS` BEFORE INSERT ON `cotisations`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=1;
END
//
DELIMITER ;
-- --------------------------------------------------------
--
-- `employes` tablosunun yapısı
--
CREATE TABLE IF NOT EXISTS `employes` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`PRENOM` varchar(20) CHARACTER SET latin1 NOT NULL,
`SS` varchar(15) CHARACTER SET latin1 NOT NULL,
`ADRESSE` varchar(50) CHARACTER SET latin1 NOT NULL,
`CP` varchar(5) CHARACTER SET latin1 NOT NULL,
`VILLE` varchar(30) CHARACTER SET latin1 NOT NULL,
`NOM` varchar(30) CHARACTER SET latin1 NOT NULL,
`VERSIONING` int(11) NOT NULL,
`INDEMNITE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `SS` (`SS`),
KEY `FK_EMPLOYES_INDEMNITE_ID` (`INDEMNITE_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=26 ;
--
-- `employes` tablosunun içeriği
--
INSERT INTO `employes` (`ID`, `PRENOM`, `SS`, `ADRESSE`, `CP`, `VILLE`, `NOM`, `VERSIONING`, `INDEMNITE_ID`) VALUES
(24, 'Marie', '254104940426058', '5 rue des oiseaux', '49203', 'St Corentin', 'Jouveinal', 1, 93),
(25, 'Justine', '260124402111742', 'La Brûlerie', '49014', 'St Marcel', 'Laverti', 1, 94);
--
-- `employes` tetikleyicileri
--
DROP TRIGGER IF EXISTS `INCR_VERSIONING_EMPLOYES`;
DELIMITER //
CREATE TRIGGER `INCR_VERSIONING_EMPLOYES` BEFORE UPDATE ON `employes`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=OLD.VERSIONING+1;
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `START_VERSIONING_EMPLOYES`;
DELIMITER //
CREATE TRIGGER `START_VERSIONING_EMPLOYES` BEFORE INSERT ON `employes`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=1;
END
//
DELIMITER ;
-- --------------------------------------------------------
--
-- `indemnites` tablosunun yapısı
--
CREATE TABLE IF NOT EXISTS `indemnites` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`ENTRETIEN_JOUR` double NOT NULL,
`REPAS_JOUR` double NOT NULL,
`INDICE` int(11) NOT NULL,
`INDEMNITES_CP` double NOT NULL,
`BASE_HEURE` double NOT NULL,
`VERSIONING` int(11) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `INDICE` (`INDICE`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=95 ;
--
-- `indemnites` tablosunun içeriği
--
INSERT INTO `indemnites` (`ID`, `ENTRETIEN_JOUR`, `REPAS_JOUR`, `INDICE`, `INDEMNITES_CP`, `BASE_HEURE`, `VERSIONING`) VALUES
(93, 2.1, 3.1, 2, 15, 2.1, 1),
(94, 2, 3, 1, 12, 1.93, 1);
--
-- `indemnites` tablosundaki tetikleyiciler
--
DROP TRIGGER IF EXISTS `INCR_VERSIONING_INDEMNITES`;
DELIMITER //
CREATE TRIGGER `INCR_VERSIONING_INDEMNITES` BEFORE UPDATE ON `indemnites`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=OLD.VERSIONING+1;
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `START_VERSIONING_INDEMNITES`;
DELIMITER //
CREATE TRIGGER `START_VERSIONING_INDEMNITES` BEFORE INSERT ON `indemnites`
FOR EACH ROW BEGIN
SET NEW.VERSIONING:=1;
END
//
DELIMITER ;
--
-- Dışa aktarılan tablolar için kısıtlamalar
--
--
-- `employes` tablosu için kısıtlamalar
--
ALTER TABLE `employes`
ADD CONSTRAINT `FK_EMPLOYES_INDEMNITE_ID` FOREIGN KEY (`INDEMNITE_ID`) REFERENCES `indemnites` (`ID`);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Aşağıdaki noktalara dikkat edilmelidir:
- 30, 73 ve 122. satırlar: Tabloların birincil anahtarları [AUTO_INCREMENT] modundadır. Bunları MySQL yönetir, EF5 değil;
- 83. satır: SS numarası bir benzersizlik kısıtlamasına sahiptir;
- 130. satır: Çalışan numarası için tekillik kısıtlaması vardır;
- 168-169. satırlar: [employes] tablosundan [indemnites] tablosuna giden yabancı anahtar;
- satır 49: [Trigger] tetikleyicisi, SGBD tarafından gömülmüş ve belirli zamanlarda çalıştırılan SQL komut dosyasıdır;
- 51-54. satırlar: [INCR_VERSIONING_COTISATIONS] tetikleyicisi, [cotisations] tablosundaki herhangi bir satırda değişiklik yapılmadan önce tetiklenir. Ardından [VERSIONING] sütunundaki değeri bir birim artırır;
- satır 59-62: [START_VERSIONING_COTISATIONS] tetikleyicisi, [cotisations] tablosuna yeni bir satır eklenmeden önce tetiklenir. Ardından [VERSIONING] sütununu 1 olarak başlatır;
- sonuç olarak, [cotisations] tablosunda bir satır oluşturulduğunda [VERSIONING] sütununun değeri 1 olur ve bu satırda yapılan her değişiklikte bu değer 1 artar. Bu mekanizma, EF5'in [cotisations] tablosundaki bir satıra erişim rekabetini şu şekilde yönetmesini sağlar:
- bir P1 süreci, [cotisations] tablosundaki L satırını T1 zamanında okur. Satırın [VERSIONING] sütunundaki değeri V1'tir;
- P2 adlı bir işlem, [cotisations] tablosundaki aynı L satırını T2 zamanında okur. Satırın sütunları [VERSIONING] ve V1 şeklindedir, çünkü P1 işlemine ait değişiklik henüz onaylanmamıştır;
- P1 süreci, L satırını değiştirir ve bu değişikliği onaylar. Bunun üzerine, [INCR_VERSIONING_COTISATIONS] tetikleyicisi nedeniyle L satırındaki [VERSIONING] sütunu, V1+1 değerine geçer;
- ardından P2 süreci de aynı işlemi gerçekleştirir. EF5, P2 sürecinde [VERSIONING] sütununda, veritabanında bulunan V1 değerinden farklı olan V1+1'den farklıdır. Bir satırı ancak [VERSIONING] değeri veritabanındakiyle aynıysa değiştirebiliriz.
Buna “eşzamanlı erişimlerin iyimser yönetimi” denir. EF5 ile, bu rolü üstlenen bir alanın [ConcurrencyCheck] notuna sahip olması gerekir.
- Benzer bir mekanizma, [employes] tablosu (satır 98-113) ve [indemnites] tablosu (satır 144-159) için de oluşturulur.
Görev: Önceki SQL komut dosyasını kullanarak MySQL ve [dbpam_ef5] veritabanlarını oluşturun. [dbpam_ef5] veritabanı, komut dosyası tarafından oluşturulmadığından önceden oluşturulmalıdır. Ardından, bu veritabanı üzerinde SQL komut dosyası çalıştırılacaktır.
9.22.2. Visual Studio Projesi
Visual Studio Express 2012 for Desktop ile, [web] katmanının oluşturulmasında kullanılan [pam-td] çözümünü yükliyoruz:
![]() |
- [1] ve VS'te, Visual Studio Express 2012 for Desktop, [pam-web-01] web projesini yükleyemiyor. Bu normaldir ve sorun teşkil etmez;
- [2]'te, [pam-td] çözümüne yeni bir proje ekleniyor;
![]() |
- [3]'te, proje [console] türündedir ve adı [4] [pam-ef5]'tir;
- [5] olarak, proje oluşturuldu. Adı kalın yazılmamış, bu nedenle çözümün başlangıç projesi değil;
![]() |
- [6] ve [7]'te, yeni projeyi başlangıç projesi olarak tanımlıyoruz.
9.22.3. Projeye gerekli referansların eklenmesi
Projeyi genel olarak ele alalım:
![]() |
Projemiz için Entity Framework 5'in DLL referanslarına ihtiyaç vardır:
- Entity Framework 5'in DLL'i;
- SGBD ve MySQL'in ADO.NET konektörüne ait DLL.
[refEF5]'in 4.2 numaralı paragrafında, [NuGet] aracını kullanarak bu DLL'leri nasıl yükleyeceğiniz açıklanmaktadır. Şu anda (Kasım 2013), Entity Framework'ün mevcut sürümü 6'dır (EF6) sürümüdür. Ne yazık ki, mevcut (Kasım 2013) SGBD ve MySQL'in ADO.NET konektörünün [NuGet] aracılığıyla temin edilebilen SGBD ve MySQL konektörleri, EF6 ile uyumlu değil. Bu nedenle, [lib] ve [1] klasörlerine,EF5 dosyasını ve [pam-ef5] projesine gerekli olan diğer DLL dosyalarını bir klasöre yerleştirdik
![]() |
[lib] klasörüne başka DLL dosyaları yerleştirdik. Bunları daha sonra kullanacağız. [2] dosyasında, bu yeni DLL dosyalarını projeye ekliyoruz.
![]() |
- [3] dosyasında, dosya sisteminde [lib] klasörüne kadar ilerleyin;
- [4]'te, üç DLL'i seçip iki kez onaylıyoruz;
- [5]'te, üç DLL dosyası proje referanslarına eklendi.
Bir DLL daha gerekiyor. Bu, makinedeki .NET çerçeve dosyaları arasında bulunacaktır.
![]() |
- [1]'te, projeye yeni bir referans ekleyin;
![]() |
- [2]'te, [Assemblys]'i seçin;
- [3]'te, [system.component] yazın;
- [4]'te, [System.ComponentModel.DataAnnotations] derlemesini seçin;
- [5]'te referans eklenmiştir.
Artık kod yazmaya ve yapılandırmaya hazırız.
9.22.4. Entity Framework varlıklar
Entity Framework varlıkları, veritabanındaki farklı tabloların satırlarını kapsayan sınıflardır. Bunları hatırlayalım:

[web] katmanında, [Employe, Cotisations, Indemnités] varlıklarını kullanmıştık (bkz. paragraf 9.7.3, sayfa 219). Bunlar, tabloların tam kopyaları değildi. Dolayısıyla [ID, VERSIONING] sütunları göz ardı edilmişti. Burada ise durum farklı olacak, çünkü bu varlıklar ORM ve EF5 tarafından kullanılıyor. Dolayısıyla, eksik özellikleri bu varlıklara ekleyeceğiz. Bu varlıkları projenin [Models] klasöründe oluşturuyoruz:
![]() |
Yeni kodları artık şu şekildedir:
[Cotisations] sınıfı
using System;
namespace Pam.EF5.Entites
{
public class Cotisations
{
public int Id { get; set; }
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
public int Versioning { get; set; }
// imza
public override string ToString()
{
return string.Format("Cotisations[{0},{1},{2},{3}, {4}, {5}]", Id, Versioning, CsgRds, Csgd, Secu, Retraite);
}
}
}
- 3. satır: ad alanı yeni projeye uyarlanmıştır;
- 7. ve 12. satırların özellikleri, [cotisations] tablosunun yapısını yansıtacak şekilde eklenmiştir;
- 17. satır: [ToString] yöntemi artık iki yeni alanı da görüntülüyor.
[Indemnites] sınıfı
using System;
namespace Pam.EF5.Entites
{
public class Indemnites
{
public int Id { get; set; }
public int Indice { get; set; }
public double BaseHeure { get; set; }
public double EntretienJour { get; set; }
public double RepasJour { get; set; }
public double IndemnitesCp { get; set; }
public int Versioning { get; set; }
// imza
public override string ToString()
{
return string.Format("Indemnités[{0},{1},{2},{3},{4}, {5}, {6}]", Id, Versioning, Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCp);
}
}
}
- 3. satır: ad alanı yeni projeye uyarlanmıştır;
- [indemnites] tablosunun yapısını yansıtmak üzere 7. ve 13. satırların özellikleri eklenmiştir;
- 18. satır: [ToString] yöntemi artık iki yeni alanı da görüntülüyor.
[Employe] sınıfı
using System;
namespace Pam.EF5.Entites
{
public class Employe
{
public int Id { get; set; }
public string SS { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Adresse { get; set; }
public string Ville { get; set; }
public string CodePostal { get; set; }
public Indemnites Indemnites { get; set; }
public int Versioning { get; set; }
// imza
public override string ToString()
{
return string.Format("Employé[{0},{1},{2},{3},{4},{5}, {6}, {7}]", Id, Versioning, SS, Nom, Prenom, Adresse, Ville, CodePostal);
}
}
}
- 3. satır: ad alanı yeni projeye uyarlanmıştır;
- [employes] tablosunun yapısını yansıtmak üzere 8. ve 16. satırların özellikleri eklenmiştir;
- 21. satır: [ToString] yöntemi artık iki yeni alanı da görüntülüyor.
ORM ve EF5 tarafından kullanılabilmeleri için, bu sınıfların özelliklerinin anotasyonlarla donatılması gerekir.
Görev: [refEF5]'in 3.4 [Création de la base à partir des entités] paragrafından yararlanarak, [Employe, Cotisations, Indemnites] varlıklarına EF5 için gerekli anotasyonları ekleyin.
İpuçları:
- burada sadece açıklamalar oluşturmanız gerekmektedir. Bahsedilen paragrafın [création de base] kısmını takip etmeyin;
- [Table] açıklaması için, [refEF5]'in 4.2. paragrafındaki MySQL örneğini takip edeceksiniz;
- [ConcurrencyCheck] özelliğindeki [Versioning] notu için, [refEF5]'in 5.2. paragrafındaki Oracle örneğini izleyin;
- [employes] tablosunun [indemnités] tablosuna sahip olduğu yabancı anahtar için, [refEF5] belgesinin 3.4.2 numaralı örneğini izleyin. Böylece [Employe] varlığına yeni bir özellik ekleyeceksiniz:
public int IndemniteId { get; set; }
ve bu özelliğin değeri, [employes] tablosundaki [INDEMNITES_ID] sütunundaki değer olacaktır. [Employe] varlığının [IndemniteId] ve [Indemnites] özelliklerine yabancı anahtar açıklamaları ekleyeceksiniz. Bunun için [refEF5]'teki 3.4.2 numaralı örneği izleyin;
- yabancı anahtarların ters ilişkilerini yönetmeyeceksiniz;
- bu iş için [refEF5] dosyasını biraz incelemeniz gerekecektir.
9.22.5. ORM ve EF5'in yapılandırılması
Projeyi bir bütün olarak ele alalım:
![]() |
[EF5] katmanı, SGBD MySQL'in [ADO.NET] konektörü aracılığıyla veritabanına erişecektir. Bu veritabanına erişmek için belirli bilgilere ihtiyaç duyar. Bu bilgiler projenin çeşitli yerlerinde bulunur.
Öncelikle veritabanı bağlamını oluşturmalıyız. Bu bağlam, [System.Data.Entity.DbContext] sistem sınıfından türetilmiş bir sınıftır. Veritabanı tablolarının nesne görüntülerini tanımlamak için kullanılır. Bu sınıfı, EF5 varlıklarıyla birlikte projenin [Models] klasörüne yerleştireceğiz:
![]() |
[DbPamContext] sınıfı şu şekilde olacaktır:
using Pam.EF5.Entites;
using System.Data.Entity;
namespace Pam.Models
{
public class DbPamContext : DbContext
{
public DbSet<Employe> Employes { get; set; }
public DbSet<Cotisations> Cotisations { get; set; }
public DbSet<Indemnites> Indemnites { get; set; }
}
}
- 6. satır: [DbPamContext] sınıfı, [DbContext] sistem sınıfından türetilmiştir;
- satır 8-10: veritabanındaki üç tablonun nesne görüntüleri. Türleri [DbSet<Entity>]'tir; burada [Entity], az önce tanımladığımız Entity Framework varlıklarından biridir. [DbSet] türü, bir varlık koleksiyonu olarak görülebilir. Bu koleksiyon, LINQ (INtegrated Sorgu Dili) ile sorgulanabilir. LINQ'i bilmeyen okuyucuların, [refEF5]'in 3.5.4 [Apprentissage de LINQ avec LINQPad] başlıklı paragrafını okumaları önerilir.
Bundan sonra [DbPamContext] sınıfını, [dbpam_ef5] veritabanının kalıcılık bağlamı olarak adlandıracağız. Bu, ORM (Nesne-İlişkisel Eşleştirici) alanında yaygın olarak kullanılan bir terimdir. Bu kalıcılık bağlamı, veritabanının bir nesne görüntüsüdür. Kalıcılık bağlamının veritabanıyla senkronizasyonundan da söz edilir: kalıcılık bağlamında yapılan değişiklikler, eklemeler ve silme işlemleri veritabanına yansıtılır. Bu senkronizasyon belirli zamanlarda gerçekleşir: kalıcılık bağlamının kapatılması sırasında, bir işlemin sonunda veya veritabanına yönelik bir SQL SELECT sorgusu öncesinde.
SGBD ve veritabanına ilişkin bilgiler [App.config]'e kaydedilir.
![]() |
[app.config]'te gerekli yapılandırma, [refEF5] dosyasının aşağıdaki paragraflarında açıklanmaktadır:
- 3.4, SGBD ve SQL Sunucusu için. Burada, EF5 yapılandırmasının temel ilkeleri belirtilmiştir;
- 4.2, SGBD ve MySQL için geçerlidir.
Bu son paragrafı takip ederek [app.config] dosyasını şu şekilde yapılandırıyoruz:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<!-- yapılandırma EF5 -->
<!-- veritabanı bağlantı dizesi [dbam_ef5] -->
<connectionStrings>
<add name="DbPamContext"
connectionString="Server=localhost;Database=dbpam_ef5;Uid=root;Pwd=;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
<!-- MySQL'in fabrika sağlayıcısı -->
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient"/>
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"
/>
</DbProviderFactories>
</system.data>
</configuration>
- 6-21. satırlar eklenmiştir. Bunlar, 2. ve 22. satırlardaki <configuration> etiketinin içine yerleştirilmelidir;
- 8-12. satırlar: veritabanı bağlantı dizelerini tanımlar; bu, ADO.NET'te açıklanan bir kavramdır ([refC#]'teki 7.3.5. paragrafa bakınız);
- 9-11. satırlar: MySQL ve [dbpam_ef5] veritabanlarına bağlantı dizesini tanımlar;
- 9. satır: bağlantı dizesinin adı. Buraya herhangi bir şey yazamazsınız. Varsayılan olarak, veritabanı bağlamını uygulayan sınıfın adını yazmanız gerekir:
public class DbPamContext : DbContext
{
public DbSet<Employe> Employes { get; set; }
public DbSet<Cotisations> Cotisations { get; set; }
public DbSet<Indemnites> Indemnites { get; set; }
}
Sınıfın adı [DbPamContext]'tir. [app.config] dosyasının 9. satırına ise [name="DbPamContext"] yazılmalıdır;
- 10. satır: SGBD ve MySQL sınıflarına özgü bir bağlantı dizesi:
- [Server=localhost]: SGBD'i barındıran makinenin IP adresi. Burada bu, yerel makine [localhost]'tir;
- [Database=dbpam_ef5;]: veritabanının adı,
- [Uid=root;]: veritabanına bağlanmak için kullanılacak kullanıcı adı,
- [Pwd=;]: bu kullanıcı adının şifresi. Burada şifre yok;
- 10. satır: [providerName="MySql.Data.MySqlClient"], kullanılacak ADO.NET konektörünün adıdır. Bu ad, 17. satırdaki [invariant] özniteliğinin adıdır. Önceki kurala uyulduğu ve aynı değişmez değere sahip bir sağlayıcı daha önce kaydedilmemiş olduğu sürece, herhangi bir ad verilebilir;
- 15-20. satırlar: ADO.NET konektör (provider) fabrikasını tanımlar. [DbProviderFactory] benim için biraz belirsiz bir kavram. Adına bakılırsa, bu, ADO.NET konektörünü oluşturabilen bir sınıf olmalı; bu konektör de SGBD'e, buradaki örnekte ise MySQL5'e erişim sağlar. Genellikle bu satırları kopyalayıp yapıştırırız. Bunlar gereklidir. 16. satırdaki [Version=6.5.4.0] özniteliğine dikkat etmeliyiz. Bu sürüm numarası, proje referanslarına eklediğiniz DLL ve [MySql.Data]'in sürüm numaralarıyla eşleşmelidir:
![]() |
- 16. satır önemlidir. Aynı ada sahip iki sağlayıcıyı birden yükleyemeyeceğimiz için, öncelikle 17. satırda yükleyeceğimiz sağlayıcıyla aynı ada sahip, halihazırda yüklü olan bir sağlayıcı varsa onu silerek başlıyoruz;
Hepsi bu kadar. İlk kez yaparken karmaşık ve anlaşılması zor gelebilir, ancak zamanla her zaman aynı işlemi tekrarladığımız için işler basitleşir.
9.22.6. [EF5] katmanının testi
[EF5] katmanımızı test etmeye hazırız. Bunu, halihazırda mevcut olan [Program.cs] programı yardımıyla yapıyoruz:
![]() |
Veritabanının içeriğini görüntüleyeceğiz. Bunu başarabilirsek, bu, yapılandırmamızın doğru olduğuna dair ilk işaret olacaktır. [refEF5]'in 3.5.3 numaralı paragrafında bir kod örneği mevcuttur. [Program.cs]'in kodu şu şekilde olacaktır:
using Pam.EF5.Entites;
using Pam.Models;
using System;
namespace Pam
{
class Program
{
static void Main(string[] args)
{
try
{
using (var context = new DbPamContext())
{
// tabloların içeriği görüntülenir
Console.WriteLine("Liste des employés ----------------------------------------");
foreach (Employe employe in context.Employes)
{
Console.WriteLine(employe);
}
Console.WriteLine("Liste des indemnités --------------------------------------");
foreach (Indemnites indemnite in context.Indemnites)
{
Console.WriteLine(indemnite);
}
Console.WriteLine("Liste des cotisations -------------------------------------");
foreach (Cotisations cotisations in context.Cotisations)
{
Console.WriteLine(cotisations);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
}
}
}
- 13. satır: BD üzerindeki tüm işlemler bu veritabanının bağlamı aracılığıyla gerçekleştirilir. Bu bağlamı [DbPamContext] sınıfı ile hayata geçirdik. Buna aynı zamanda veritabanının kalıcılık bağlamı da adını verdik;
- 13. ve 31. satırlar: Kalıcılık bağlamı üzerindeki işlemler, bir [using] cümlesi içinde gerçekleştirilir. Kalıcılık bağlamı, [using] cümlesinin başında açılır ve bu cümleden çıkıldığında otomatik olarak kapatılır. Bu, [using] cümlesinde kalıcılık bağlamı üzerinde yapılan tüm değişikliklerin, cümleden çıkıldığında veritabanına yansıtılacağı anlamına gelir. Ardından, bir işlem içinde SQL komut dizisi BD'e gönderilir. Bu, bir SQL komutunun başarısız olması durumunda, daha önce gönderilen tüm SQL komutlarının iptal edileceği anlamına gelir. Bu durumda EF5 tarafından bir istisna atılır;
- 17. satır: [context.Employes] ifadesi, [employes] tablosunun nesne görüntüsünü belirtir. [Employes]'in, [DbPamContext] kalıcılık bağlamının bir özelliği olduğu hatırlatılmalıdır:
public class DbPamContext : DbContext
{
public DbSet<Employe> Employes { get; set; }
public DbSet<Cotisations> Cotisations { get; set; }
public DbSet<Indemnites> Indemnites { get; set; }
}
- 17. satır: [foreach]'in [context.Employes] koleksiyonunu taraması, veritabanındaki tüm çalışanları kalıcılık bağlamına geri getirecektir. Böylece EF5 tarafından SQL SELECT emri verilecektir;
- 17-20. satırlar: çalışan koleksiyonu taranır ve 19. satırda, çalışanları konsolda görüntülemek için [Employe] sınıfının [ToString] yöntemi kullanılır;
- 21-25. satırlar: tazminat koleksiyonu için de aynı işlem yapılır;
- satır 27-30: prim koleksiyonu için de aynı işlem yapılır.
[Employe] varlığının tanımına geri dönelim:
using System;
namespace Pam.EF5.Entites
{
public class Employe
{
public int Id { get; set; }
public string SS { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Adresse { get; set; }
public string Ville { get; set; }
public string CodePostal { get; set; }
public Indemnites Indemnites { get; set; }
public int Versioning { get; set; }
// imza
public override string ToString()
{
return string.Format("Employé[{0},{1},{2},{3},{4},{5}, {6}, {7}]", Id, Versioning, SS, Nom, Prenom, Adresse, Ville, CodePostal);
}
}
}
- satır 15: Bir çalışanın bir tazminat referansı vardır.
Bir çalışanı kalıcılık bağlamına geri getirirken, tazminatını da beraberinde getirir miyiz? Varsayılan olarak cevap hayırdır. Bu, [Lazy Loading] kavramıdır. Başka bir varlık içinde referans verilen varlıklar, bu diğer varlıkla birlikte kalıcılık bağlamına aktarılmaz. Bunlar, yalnızca açık bir kalıcılık bağlamı içinde kod tarafından talep edildiklerinde aktarılır. Kalıcılık bağlamı kapalıysa, bir istisna atılır.
Dolayısıyla, [ToString] yöntemi, [Indemnites] özelliğine aşağıdaki gibi başvurmuş olsaydı:
// imza
public override string ToString()
{
return string.Format("Employé[{0},{1},{2},{3},{4},{5},{6},{7},{8}]", Id, Versioning, SS, Nom, Prenom, Adresse, Ville, CodePostal, Indemnites);
}
[Program.cs]'teki aşağıdaki işlem:
foreach (Employe employe in context.Employes)
{
Console.WriteLine(employe);
}
sadece çalışanları değil, aynı zamanda tazminatlarını da kalıcı veritabanına aktarmış olurdu; çünkü 3. satırda [Employe.ToString] yöntemi çağrılıyor ve bu yöntem [Indemnites] varlığına başvuruyor.
[Program.cs]'in çalıştırılması şu sonuçları verir:
Çalışmazsa ne yapmalı? Durumunuz kötü... Birçok olası hata kaynağı vardır:
- EF5'in yapılandırmasını kontrol edin (bölüm 9.22.5);
- Entity Framework varlıklarınızı kontrol edin (bölüm 9.22.4).
9.22.7. [EF5] katmanındaki DLL
Projemizi bir sınıf kütüphanesi haline getiriyoruz, böylece derleme sırasında bir .exe dosyası yerine bir .dll derlemesi oluşturulur. Bu işlem, simüle edilmiş iş katmanı için 9.7.6 numaralı paragrafta görüldüğü gibi proje özelliklerinden gerçekleştirilir.
Görev: [pam-ef5] projesinin türünü sınıf kütüphanesi olarak değiştirin ve ardından projeyi yeniden derleyin.
9.23. Adım 16: [DAO] katmanının kurulması
9.23.1. [DAO] katmanının arayüzü
![]() |
Simüle edilmiş [métier] katmanında yaptığımız gibi, [DAO] katmanına da bir arayüz üzerinden erişilebilecektir. Bu arayüz ne olacak?
Daha önce oluşturduğumuz simüle edilmiş [métier] katmanının [IPamMetier] arayüzüne bir göz atalım:
public interface IPamMetier {
// tüm çalışan kimliklerinin listesi
Employe[] GetAllIdentitesEmployes();
// ------- maaş hesaplaması
FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
3. satırda, [GetAllIdentitesEmployes] yöntemi, ana sayfanın açılır menüsünü doldurmak için kullanılır:
![]() |
Bu çalışanlar veritabanında aranmalıdır.
6. satırda, [GetSalaire] yöntemi, SS numarasına sahip bir çalışanın maaş bordrosunu hesaplamaya yarar. [FeuilleSalaire] türünün tanımını hatırlayalım:
public class FeuilleSalaire
{
// otomatik özellikler
public Employe Employe { get; set; }
public Cotisations Cotisations { get; set; }
public ElementsSalaire ElementsSalaire { get; set; }
}
5. ve 6. satırlardaki bilgiler veritabanından alınacaktır. Bir çalışanın [Indemnites] özelliğine sahip olduğunu hatırlayalım. Bu bilgi de aktarılmalıdır.
Dolayısıyla, [DAO] katmanı için şu arayüzle başlayabiliriz:
public interface IPamDao {
// tüm çalışan kimliklerinin listesi
Employe[] GetAllIdentitesEmployes();
// belirli bir çalışan ve ek ödemeleri
Employe GetEmploye(string ss);
// tüm primlerin listesi
Cotisations GetCotisations();
}
9.23.2. Visual Studio projesi
Görev: [pam-td] çözümüne, [console] türünde ve [pam-dao] adında yeni bir proje ekleyin. Bu projeyi çözümün başlangıç projesi olarak ayarlayın.
![]() |
9.23.3. Projeye gerekli referansların eklenmesi
Projeyi genel olarak ele alalım:
![]() |
[pam-dao] projesinin belirli sayıda DLL'e ihtiyacı vardır:
- [pam-ef5] projesinde referans verilenlerin tümü;
- [pam-ef5] projesine ait olan.
Ayrıca, [Spring.net]'i, [DAO] katmanını oluşturmak için kullanacağız. Bunun için DLL, [Spring.core] ve [Common.Logging]'e ihtiyacımız var. Bu DLL dosyaları, vaka çalışması destek materyalindeki [lib] klasöründe bulunmaktadır.
Görev: Bu farklı referansları [pam-dao] projesine ekleyin.
![]() |
9.23.4. [DAO] katmanının uygulanması
![]() |
Yukarıda, [PamException] sınıfı, 9.7.4. paragrafında tanımlanan sınıftır. Sadece ad alanını değiştiriyoruz (aşağıdaki 1. satır):
namespace Pam.Dao.Entites
{
// istisna sınıfı
public class PamException : Exception
{
....
}
}
[IPamDao] arayüzü, 9.23.1. paragrafta az önce tanımladığımız arayüzdür:
using Pam.EF5.Entites;
namespace Pam.Dao.Service
{
public interface IPamDao
{
// tüm çalışan kimliklerinin listesi
Employe[] GetAllIdentitesEmployes();
// belirli bir çalışan ve tazminatları
Employe GetEmploye(string ss);
// tüm primlerin listesi
Cotisations GetCotisations();
}
}
[PamDaoEF5] sınıfı, ORM ve EF5'i kullanarak bu arayüzü uygular. Kod şöyledir:
using Pam.Dao.Entites;
using Pam.EF5.Entites;
using Pam.Models;
using System;
using System.Linq;
namespace Pam.Dao.Service
{
public class PamDaoEF5 : IPamDao
{
// özel alanlar
private Cotisations cotisations;
private Employe[] employes;
// Oluşturucu
public PamDaoEF5()
{
// prim
try
{
....
}
catch (Exception e)
{
throw new PamException("Erreur système lors de la construction de la couche [DAO]", e, 1);
}
}
// GetCotisations
public Cotisations GetCotisations()
{
return cotisations;
}
// GetAllIdentitesEmploye
public Employe[] GetAllIdentitesEmployes()
{
return employes;
}
// GetEmploye
public Employe GetEmploye(string SS)
{
try
{
....
catch (Exception e)
{
throw new PamException(string.Format("Erreur système lors de la recherche de l'employé [{0}]", SS), e, 2);
}
}
}
}
Not:
- 10. satır: [PamDaoEF5] sınıfı, [IPamDao] arayüzünü uygular;
- [cotisations] ve [employes] tabloları, 13-14. satırlardaki özelliklerde önbelleğe alınmıştır. Çalışanların ek ödemeleri yoktur;
- 17-28. satırlar: 13-14. satırları başlatma işlemini yapıcı gerçekleştirir;
- 43-52. satırlar: [GetEmploye] yöntemi, bir çalışanı ödenekleriyle birlikte döndürür. Bu yöntem, parametre olarak söz konusu çalışanın sosyal güvenlik numarasını alır. Çalışan veritabanında mevcut değilse, yöntem işaretçiyi null olarak döndürür.
Görev: [PamDaoEF5] sınıfının kodunu tamamlayın.
Oluşturucu için, 9.22.6 numaralı paragrafta sunulan [EF5] katmanının test kodundan yararlanılacaktır. [GetEmploye] yöntemi için, 3.5.7 numaralı paragrafta yer alan [Eager and Lazy loading] ve [refEF5] örneklerinden yararlanılacaktır.
9.23.5. [DAO] katmanının yapılandırılması
9.22.5 numaralı paragrafta yapıldığı gibi, projenin [App.config] dosyasında EF5'i yapılandırmamız gerekir:
![]() |
Ödev 1: EF5'i [App.config] içinde yapılandırın. Bunun için, [EF5] katmanındaki [App.config] dosyasında yapılanları tekrarlamanız yeterlidir.
Test programımız, [DAO] katmanına ilişkin bir referans elde etmek için [Spring.net] dosyasını kullanacaktır.
Görev 2: 9.20.2 numaralı paragrafta yapılanlardan yararlanarak, [pam-dao] projesindeki [app.config] yapılandırma dosyasını, az önce oluşturduğumuz [PamDaoEF5] sınıfıyla ilişkili [pamdao] adlı bir Spring nesnesini tanımlayacak şekilde değiştirin. [app.config] ve [web.config] dosyaları aynı yapıya sahiptir. <configSections> etiketinin, kök <configuration> etiketinden sonra gelen ilk etiket olmasına dikkat edilmelidir.
9.23.6. [DAO] katmanının testi
[DAO] katmanımızı test etmeye hazırız. Bunu, halihazırda mevcut olan [Program.cs] programı yardımıyla yapıyoruz:
![]() |
[DAO] katmanının arayüzündeki çeşitli işlevleri test edeceğiz. [Program.cs] kodunun içeriği şu şekilde olacaktır:
using Pam.Dao.Service;
using Pam.EF5.Entites;
using Spring.Context.Support;
using System;
namespace Pam.Dao.Tests
{
public class Program
{
public static void Main()
{
try
{
// [dao] katmanının örneklenmesi
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// çalışan kimlik listesi
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes())
{
Console.WriteLine(Employe.ToString());
}
// bir çalışan ve ödenekleri
Console.WriteLine("------------------------------------");
Employe e = pamDao.GetEmploye("254104940426058");
Console.WriteLine("employé= {0}, indemnités={1}", e, e.Indemnites);
Console.WriteLine("------------------------------------");
// var olmayan bir çalışan
Employe employe = pamDao.GetEmploye("xx");
Console.WriteLine("Employé n° xx");
Console.WriteLine((employe == null ? "null" : employe.ToString()));
Console.WriteLine("------------------------------------");
// prim listesi
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
}
catch (Exception ex)
{
// istisna ekranı
Console.WriteLine(ex.ToString());
}
//ara
Console.ReadLine();
}
}
}
- 15. satır: [Spring.net] sayesinde [DAO] katmanına bir referans elde ediyoruz.
Bu programın çalıştırılması sonucunda elde edilen sonuçlar şunlardır:
9.23.7. DLL, [DAO] katmanından
Görev: [pam-dao] projesinin türünü sınıf kütüphanesine dönüştürün ve ardından projeyi yeniden oluşturun (9.22.7 numaralı paragrafta yapılanları tekrarlayın).
9.24. Adım 17: [métier] katmanının kurulması
9.24.1. [métier] katmanının arayüzü
![]() |
[métier] katmanının arayüzü, 9.7.2. paragrafında oluşturduğumuz simüle edilmiş [métier] katmanının [IPamMetier] arayüzü olacaktır.
public interface IPamMetier {
// tüm çalışan kimliklerinin listesi
Employe[] GetAllIdentitesEmployes();
// ------- maaş hesaplaması
FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
9.24.2. Visual Studio projesi
Görev: [pam-td] çözümüne, [console] türünde ve [pam-metier] adında yeni bir proje ekleyin. Bu projeyi çözümün başlangıç projesi olarak ayarlayın.
![]() |
9.24.3. Projeye gerekli referansların eklenmesi
Projeyi genel bağlamda ele alalım:
![]() |
[pam-metier] projesi, DLL'lerden belirli bir sayıda gerektirir:
- [pam-dao] ve [pam-ef5] projelerinde referans verilenlerin tümü;
- [pam-dao] ve [pam-ef5] projelerinin kendilerindekiler.
Görev: Bu farklı referansları [pam-metier] projesine ekleyin.
![]() |
9.24.4. [métier] katmanının uygulanması
![]() |
Yukarıda, simüle edilen [métier] katmanında (bkz. paragraf 9.7) halihazırda kullanılmış dört öğe bulunmaktadır. Bu farklı sınıflar tarafından içe aktarılan ad alanlarında değişiklikler olabilir. Bunları yönetin. [PamMetier] sınıfı, [IPamMetier] arayüzünü aşağıdaki şekilde uygular:
using Pam.Dao.Service;
using Pam.EF5.Entites;
using Pam.Metier.Entites;
using System;
namespace Pam.Metier.Service
{
public class PamMetier : IPamMetier
{
// Spring tarafından başlatılan [DAO] katmanına ilişkin referans
public IPamDao PamDao { get; set; }
// tüm çalışan kimliklerinin listesi
public Employe[] GetAllIdentitesEmployes()
{
...
}
// belirli bir çalışan ve ek ödemeleri
public Employe GetEmploye(string ss)
{
...
}
// sosyal güvenlik primleri
public Cotisations GetCotisations()
{
...
}
// maaş hesaplaması
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés)
{
// SS: çalışanın SS numarası
// HeuresTravaillées: çalışılan saat sayısı
// Çalışılan Günler: çalışılan gün sayısı
...
}
}
- 13. satır: [DAO] katmanına bir referans vardır. Bu referans, [PamMetier] sınıfının örneklendirilmesi sırasında Spring tarafından başlatılacaktır. Dolayısıyla, çeşitli yöntemler çalıştırıldığında 13. satır zaten başlatılmış olacaktır.
Görev: [PamMetier] sınıfının kodunu tamamlayın. [GetSalaire]'te ss numaralı çalışanın mevcut olmadığı tespit edilirse, bir [PamException] başlatılacaktır. Maaş hesaplama yöntemi 9.5. paragrafında açıklanmıştır. Tüm ara hesaplamaların virgül sonrası iki basamağa yuvarlanmasına dikkat edilmelidir.
9.24.5. [métier] katmanının yapılandırılması
9.22.5. paragrafta yapıldığı gibi, projenin [app.config] dosyasında EF5'i yapılandırmamız gerekir:
![]() |
Ödev 1: EF5'i [app.config] içinde yapılandırın. Bunun için, [EF5] katmanındaki [app.config] dosyasında yapılanları tekrarlamanız yeterlidir.
Test programımız, [métier] katmanına ilişkin bir referans elde etmek için [Spring.net] dosyasını kullanacaktır.
Görev 2: Daha önce 9.23.5 numaralı paragrafta yaptıklarınızdan yararlanarak, [pam-metier] projesindeki [app.config] yapılandırma dosyasını, az önce oluşturduğumuz [PamMetier] sınıfıyla ilişkili [pammetier] adlı bir Spring nesnesini tanımlayacak şekilde değiştirin. En kolay yol, [pam-dao] projesindeki [app.config] dosyasını kopyalayıp eksik kısımları eklemektir.
Burada bir zorluk var. Sadece [métier] katmanını [PamMetier] sınıfıyla örneklendirmekle kalmayıp, aynı zamanda [PamDao] özelliğini de başlatmak gerekiyor:
// Spring tarafından başlatılan [DAO] katmanına referans
public IPamDao PamDao { get; set; }
[app.config]'teki Spring yapılandırması şu şekildedir:
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pamdao" type=" Pam.Dao.Service.PamDaoEF5, pam-dao"/>
<object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier">
<property name="PamDao" ref="pamdao" />
</object>
</objects>
</spring>
- 6. satır: [PamDaoEF5] sınıfıyla ilişkili [pamdao] nesnesini tanımlar;
- 7. satır: [PamMetier] sınıfıyla ilişkili [pammetier] nesnesini tanımlar;
- satır 8: [property] etiketi, [PamMetier] sınıfının bir genel özelliğini başlatmak için kullanılır. [name="PamDao"] özniteliği, [PamMetier] sınıfında başlatılacak özelliğin adına karşılık gelir. [ref="pamdao"] özniteliği, özelliğin bir referansla başlatıldığını gösterir; bu referans, 6. satırdaki [pamdao] nesnesine, dolayısıyla [DAO] katmanına aittir. İşte istediğimiz de buydu.
9.24.6. [métier] katmanının testi
[métier] katmanımızı test etmeye hazırız. Bunu, halihazırda mevcut olan [Program.cs] programı yardımıyla yapıyoruz:
![]() |
[métier] katmanının arayüzündeki çeşitli işlevleri test edeceğiz. [Program.cs] kodunun içeriği şu şekilde olacaktır:
using System;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
using Pam.EF5.Entites;
namespace Pam.Metier.Tests
{
public class Program
{
public static void Main()
{
try
{
// [métier] katmanının örneklenmesi
IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
// çalışan kimlik listesi
Console.WriteLine("Employés -----------------------------");
foreach (Employe Employe in pamMetier.GetAllIdentitesEmployes())
{
Console.WriteLine(Employe);
}
// maaş bordro hesaplamaları
Console.WriteLine("salaires -----------------------------");
Console.WriteLine(pamMetier.GetSalaire("260124402111742", 30, 5));
Console.WriteLine(pamMetier.GetSalaire("254104940426058", 150, 20));
try
{
Console.WriteLine(pamMetier.GetSalaire("xx", 150, 20));
}
catch (PamException ex)
{
Console.WriteLine(string.Format("PamException : {0}", ex.Message));
}
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Exception : {0}, Exception interne : {1}", ex.Message, ex.InnerException == null ? "" : ex.InnerException.Message));
}
// ara
Console.ReadLine();
}
}
}
- 16. satır: [Spring.net] sayesinde [métier] katmanına bir referans elde ediyoruz.
Bu programın çalıştırılması sonucunda elde edilen sonuçlar şunlardır:
9.24.7. DLL, [métier] katmanından
Görev: [pam-metier] projesinin türünü sınıf kütüphanesine dönüştürün ve ardından projeyi yeniden oluşturun (9.22.7 numaralı paragrafta yapılanları tekrarlayın).
9.25. Adım 18: [web] katmanının kurulması
Mimari yapımızın son katmanına, [web] katmanına geldik:
![]() |
Simüle edilmiş bir [métier] katmanının yardımıyla geliştirdiğimiz [web] katmanını yeniden kullanacağız.
9.25.1. Visual Studio Projesi
Web katmanımızı az önce geliştirdiğimiz [métier, DAO, EF5] katmanlarına bağlamak için Visual Studio Express 2012 for Web'e geri dönüyoruz. Burada esas olarak yapılandırma işlemleri ve birkaç ad alanı değişikliği yapmamız gerekiyor.
Visual Studio Express 2012 for Web ile [pam-td] çözümünü yükleyin:
![]() |
- [1]'e, [pam-td] çözümünü ise VS Web Studio'ya yükleyin. [pam-web-01] web projesi yeniden görünür hale gelir. Bu proje, masaüstü sürümü VS Studio'da kaybolmuştu.
- [pam-web-01] web projesinin yapılandırması değiştirilmelidir. Çalışan bir projeyi değiştirmek yerine, bu projenin bir kopyası üzerinde değişiklikler yapacağız. Öncelikle [2]'te, projeyi çözümden sileriz (bu işlem dosya sisteminde hiçbir şeyi silmez).
![]() |
- [3]'te, Windows Gezgini'ni kullanarak [pam-web-01] klasörünü [pam-web-02]'e kopyalayacağız;
- [4]'te, [pam-web-02] projesini [pam-td] çözümüne yükleyin. Proje, [pam-web-01] adıyla eklenir;
- [5]'te, bu adı [pam-web-02] olarak değiştirin ve bu projeyi başlangıç projesi olarak ayarlayın;
![]() |
- adını [6] olarak değiştirin ve eski proje olan [pam-web-01]'i yükleyin. Artık tüm projeleriniz elinizde. [pam-web-02] ile çalışmaya özen gösterin.
9.25.2. Projeye gerekli referansların eklenmesi
Projeyi genel olarak ele alalım:
![]() |
[pam-web-02] projesi, belirli sayıda DLL'e ihtiyaç duyar:
- [pam-metier], [pam-dao] ve [pam-ef5] projeleri tarafından referans verilenlerin tümü;
- [pam-metier], [pam-dao] ve [pam-ef5] projelerinin kendilerinde yer alanlar.
Görev: Bu farklı referansları [pam-web-02] projesine ekleyin. [pam-metier-simule] projesindeki referans kaldırılmalıdır. [métier] katmanına geçiyoruz. Referanslar arasında halihazırda bazı DLL referansları bulunmaktadır. Bunları silin ve ardından eklemelerinizi yapın.
![]() |
9.25.3. [web] katmanının uygulanması
[pam-web-02] projesini oluşturun. Aşağıdaki gibi hatalar görünecektir:
![]() |
[ApplicationModel] sınıfı, [Employe] türünü kullanıyor. Simüle edilen [métier] katmanında, bu tür [Pam.Metier.Entites] ad alanında tanımlanmıştı. Artık [Pam.EF5.Entites] ad alanında yer almaktadır. Bu hataları yukarıda gösterildiği gibi düzeltin.
9.25.4. [web] katmanının yapılandırılması
9.24.5 numaralı paragrafta yapıldığı gibi, projenin [web.config] dosyasında EF5'i yapılandırmamız gerekiyor:
![]() |
Görev 1: [web.config] dosyasının mevcut tüm içeriğini, [pam-metier] projesindeki [app.config] dosyasının içeriğiyle değiştirin.
Web uygulamamızdaki [Global.asax] dosyası, [Spring.net] dosyasını kullanarak [métier] katmanından bir referans almaktadır:
try
{
// [métier] katmanının örneklenmesi
application.PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
catch (Exception ex)
{
application.InitException = ex;
}
4. satırda, [pammetier] adlı Spring nesnesine ilişkin bir referans istenmektedir. [métier] katmanına verilen ad tam olarak budur (bunu [web.config] dosyanızda kontrol edin).
9.25.5. [web] katmanının testi
[web] katmanımızı test etmeye hazırız. Öncelikle çalışma bağlantı noktasını değiştireceğiz. Varsayılan olarak, [pam-web-02], [pam-web-01]'in yapılandırmasına sahiptir ve dolayısıyla aynı bağlantı noktasında çalışır. Deneyimlerimiz, bunun sorun yarattığını gösteriyor: IIS, bu durumda [pam-web-01] projesinin kodlarını kullanmaya devam ediyor. Aşağıdaki adımları izleyin:
![]() |
![]() |
[4]'te bağlantı noktası numarasını değiştirin; örneğin, birler basamağındaki rakamı değiştirin.
[pam-web-02] projesini [Ctrl-F5] ile çalıştırın. Böylece aşağıdaki ana sayfa görüntülenir:
![]() |
[1]'te, [dbpam_ef5] veritabanındaki çalışanlar elde edilir. Simüle ettiğimiz [métier] katmanında bulunan [X X] numaralı çalışanın artık mevcut olmadığına dikkat edelim. Bir simülasyon yapalım:
![]() |
[2]'te artık hayali bir maaş değil, gerçek maaşı elde ediyoruz. Şimdi SGBD ve MySQL5'i durdurup başka bir simülasyon yapalım:
![]() |
[3]'te, bazı mesajlar İngilizce olsa da okunabilir bir hata sayfası elde ettik. Şimdi MySQL'i tekrar durdurup, uygulamayı VS'ten [Ctrl-F5]'e kadar yeniden çalıştıralım:
![]() |
9.20.4 numaralı paragrafta oluşturulan [initFailed.cshtml] görünümü karşımıza çıkar. Bu görünüm, istisna yığınındaki hata mesajlarını gösterir. Okuyucunun başka testler yapması önerilir.
9.26. Adım 19: ASP.NET uygulamasını İnternet üzerinden erişilebilir hale getirme
Visual Studio ile bir ASP.NET uygulaması geliştirildiğinde, varsayılan yapılandırma nedeniyle geliştirilen uygulamaya yalnızca [localhost] adresinden erişilebilir. Diğer tüm adresler, Visual Studio'nun yerleşik sunucusu tarafından reddedilir ve bu durumda [400 Bad Request] hatası döndürülür.
Bunu şu şekilde görebilirsiniz:
- DOS penceresinde, geliştirme makinenizin IP adresini not edin:
Microsoft Windows [version 6.3.9600]
(c) 2013 Microsoft Corporation. Tous droits réservés.
dos>ipconfig
Configuration IP de Windows
Carte Ethernet Connexion au réseau local :
Suffixe DNS propre à la connexion. . . : ad.univ-angers.fr
Adresse IPv6 de liaison locale. . . . .: fe80::698b:455a:925:6b13%4
Adresse IPv4. . . . . . . . . . . . . .: 172.19.81.34
Masque de sous-réseau. . . . . . . . . : 255.255.0.0
Passerelle par défaut. . . . . . . . . : 172.19.0.254
Carte réseau sans fil Wi-Fi :
Statut du média. . . . . . . . . . . . : Média déconnecté
Suffixe DNS propre à la connexion. . . :
IP adresi burada 14. satırda gösterilmektedir. Wi-Fi bağlantınız varsa, cihazın Wi-Fi adresi 20. satır ve sonrasında görünecektir.
- [clic droit sur projet / propriétés / onglet web] projesinin özelliklerini kontrol edin:
![]() |
Uygulama, [localhost] makinesinin [65010] bağlantı noktasında çalışacaktır.
- Projenizi [Ctrl-F5] ile çalıştırın

- [localhost]'i, aşağıdaki makinenin IP adresiyle değiştirin:

Sunucu, [400 Bad Request] yanıtını gönderdi. Visual Studio tarafından kullanılan IIS Express sunucusu yalnızca [localhost] adını kabul eder.
Geliştirilen uygulamayı URL türündeki bir [http://adresseIP/contexte/...]'e erişilebilir hale getirmek için, IIS Express dışında başka bir sunucu, örneğin bir IIS (Express olmayan) sunucusu kullanılmalıdır. Bu sunucunun varlığını kontrol etmek için (genellikle Windows Pro sürümlerinde bulunur), [Panneau de configuration\Système et sécurité\Outils d’administration] kontrol paneline gitmelisiniz:

Bu seçenek her zaman mevcut olmayabilir. Bu durumda [ Panneau de configuration \ Programmes]'e gidip Web Yönetim Araçları'nı yüklemeniz gerekir.
![]() |
[Gestionnaire des services internet (IIS)] seçeneği göründüğünde, bunu etkinleştirin:
![]() |
Varsayılan web sitesini başlatırız. Bunun için önceden [Service de publication World Wide Web] hizmetinin başlatılmış olması gerekir:
![]() |
Bu işlem tamamlandıktan sonra, bir tarayıcıyla URL [http://localhost] adresine erişin. Öncesinde, 80 numaralı bağlantı noktasının başka bir web sunucusu tarafından kullanılmadığını kontrol edin. Kullanılıyorsa, onu durdurun.
![]() |
IIS sunucusu yanıt verdi. Şimdi [localhost]'i, bilgisayarınızın IP adresiyle değiştirin:
![]() |
Çalışıyor. Şimdi Visual Studio'ya geri dönelim:
- öncelikle, Visual Studio'yu [administrateur] modunda başlatmalısınız
![]() |
Bunu yaptıktan sonra, dağıtmak istediğiniz web projesinin yapılandırmasını [clic droit sur projet / propriétés / onglet web] olarak değiştirmeniz gerekir:
![]() |
Dağıtım sunucusu olarak yerel sunucuyu seçmelisiniz. Visual Studio, uygulamanın adını otomatik olarak belirler. Bunu değiştirebilirsiniz. Projeyi şu komutla çalıştırın:
![]() |
Şimdi [localhost]'i, bilgisayarınızın IP adresiyle değiştirin:
![]() |
IIS sunucusu mevcut değilse, URL ve [http://ultidev.com/Download/ ] adreslerinde bulunan [Ultidev Web Server Pro] gibi ücretsiz bir ASP.NET sunucusu kullanılabilir. Kurulum tamamlandıktan sonra, bu sunucu ile bir web uygulamasını çalıştırmanın iki yöntemi vardır:
Hızlı yöntem
Bir Windows gezgini açın ve dağıtılacak ASP.NET uygulamasının klasörünü seçin:
![]() |
Böylece web sunucusu başlatılır ve web uygulaması bir tarayıcıda görüntülenir:
![]() |
- [3]'te web sunucusunu durdurabilir veya başlatabilirsiniz;
- [4]'te, web uygulamasının hizmet bağlantı noktasını değiştirebilirsiniz;
Sunucuyu başlatmadan önce, aşağıdaki [UWS HiPriv Services] hizmetinin başlatılmış olması gerekir:
![]() |
Sunucu başlatıldığında arayüz şu şekilde görünür:
![]() |
[6] bağlantısına tıklandığında uygulamanın ilk sayfası görüntülenir:
![]() |
Böylece, [localhost] yerine makinenin IP adresini girebilirsiniz:
![]() |
Dolayısıyla burada da yalnızca [localhost] adı kabul edilir.
Uzun yol
Ultidev Web Explorer uygulamasını başlatın
![]() |
ve ardından aşağıdaki adımları izleyin:
![]() |
![]() |
![]() |
- [8]'te, dağıtılacak web uygulamasının klasörünü belirtin;
![]() |
- [10-11] nedeniyle, web uygulaması URL ve [http://localhost:81/] ile çağrılmalıdır;
![]() |
![]() |
- web sunucusunu [14] ile başlatın;
![]() |
- URL ve [19]'i isteyin;
![]() |
- [20]'te, [localhost] adını kullanmak yerine, makinenin yerel adresi olan IP'i kullanarak istenen sayfaya ulaşıldı. Aradığımız şey buydu;
Ultidev sunucusu, otomatik olarak başlayan bir Windows hizmeti olarak kurulmuştur. Ultidev sunucusunun otomatik başlatılmasını aşağıdaki şekilde devre dışı bırakabilirsiniz:
- [Panneau de configuration\Système et sécurité\Outils d’administration] seçeneğini seçin;
![]() |
- [1, 2]: [Ultidev Web Server Pro] hizmetinin özelliklerini seçin;
- [3]: Başlatma türünü "Manuel" olarak ayarlayın.
Sunucuyu manuel olarak başlatmak için örneğin [Ultidev Web Explorer] uygulamasını kullanın:
![]() |
9.27. Adım 20: Android için yerel uygulama oluşturma
APU türünde bir web uygulamanız (Tek Sayfalı Uygulama) varsa, [Phonegap] ve [http://phonegap.com/] araçlarını kullanarak mobil cihazlar (Android, IoS, Windows 8, ...) için bir yürütülebilir dosya oluşturmak mümkündür. Bunu yapmanın başka yolları da vardır; özellikle Apache Cordova [https://cordova.apache.org/] adlı açık kaynaklı ürün kullanılarak. Phonegap sitesinde çevrimiçi olarak bulunan araç [http://build.phonegap.com/apps], dönüştürülecek sitenin zip dosyasını "yükler". Ana sayfanın adı [index.html] olmalı ve statik bir sayfa olmalıdır, yani bir web çerçevesi tarafından oluşturulmuş olmamalıdır (ASP.NET, JEE, PHP, ...) tarafından oluşturulmamalıdır. Öncelikle bunu oluşturmaya başlayacağız.
9.27.1. Uygulamanın mimarisi
Burada, bir Android uygulaması oluşturmak istediğimizi unutmamak gerekir. Bu tür bir uygulama genellikle şu mimariye sahiptir:
![]() |
- [1] örneğinde, kullanıcı bir veya daha fazla web hizmeti ([2]) ile iletişim kuran bir Android tablet kullanır;
APU modeline geri dönelim:
![]() |
- tarayıcıya bir başlangıç sayfası yüklenir (yukarıdaki şemada bu sayfanın nereden geldiği belirtilmemiştir);
- sonraki sayfalar [1-4] Ajax çağrıları ile alınır. Tarayıcı tarafından yeni bir sayfa yüklenmez;
İlk görünüm, Ajax çağrılarıyla elde edilen diğer görünümlerle aynı sunucu tarafından sağlanabilir veya sağlanmayabilir. Aynı sunucu tarafından sağlanmıyorsa, ilk sayfanın JavaScript'i, diğer görünümleri sunacak web sunucusunun URL değerini bilmelidir. Bu durum, geliştireceğimiz Android uygulamasında geçerli olacaktır:
![]() |
- [index.html] adlı statik sayfa, tarayıcı işlevlerine sahip ve dolayısıyla [index.html] sayfasında yer alan JavaScript'i çalıştırabilen [1] adlı bir Android yerel uygulaması içine kapsüllenecektir;
- bu sayfa, [2] sunucusuna Ajax çağrıları yaparak diğer görünümleri alacaktır. Bunun için web sunucusunun URL adresini bilmesi gerekir;
Uygulamayı bu şekilde çalışacak şekilde yeniden düzenleyeceğiz. Böylece ilk sayfa şu şekilde olacaktır:
![]() |
- [1]'te, uygulamanın başlangıç sayfasındaki URL. Bu değer, 9.26. paragrafta incelenen Ultidev sunucusu tarafından bize sağlanacaktır;
- [2]'te, kullanıcı maaş simülatörünün URL değerini girmelidir. Bunu ana sayfanın JavaScript koduna sabit olarak girebiliriz, ancak bu durum testleri zorlaştırır: IP adres simülatörünü (veya bağlantı noktasını) değiştirdiğimiz anda, bunu JavaScript kodunda da değiştirmemiz gerekir;
- [3] olarak değiştirildiğinde, bir sonraki sayfayı yükleyecek olan [Connexion] bağlantısı:
![]() |
- [4]'te, tarayıcının URL'i değişmediğine dikkat edilmelidir. Bu, her zaman başlangıç sayfasının adresidir ve uygulamanın ömrü boyunca bu şekilde kalacaktır.
Bu görünüm elde edildikten sonra her şey önceki gibi çalışır: farklı görünümler Ajax çağrılarıyla elde edilir. Göreceğiz ki çok az kodun değiştirilmesi gerekecektir.
9.27.2. [pam-web-02] projesinin yeniden yapılandırılması
[pam-web-02] projesinin [Content] klasörü içinde, aşağıdaki gibi [bootstrap] klasörünü (adı önemli değil) oluşturuyoruz:
![]() |
Bu klasöre [index.html] adlı statik sayfayı ve bu sayfanın ihtiyaç duyduğu tüm kaynakları (CSS ve JS dosyaları) ekledik. [index.html] sayfası, Visual Studio projesindeki [_Layout.cshtml] ana sayfasının kodunu alır ve statik olmayan her şeyi ortadan kaldırır. Bu da aşağıdaki kodu verir:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simulateur de paie</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="Site.css" />
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="jquery.validate.min.js"></script>
<script type="text/javascript" src="jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="globalize.js"></script>
<script type="text/javascript" src="globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="jquery.unobtrusive-ajax.min.js"></script>
<script type="text/javascript" src="myScripts.js"></script>
</head>
<body>
<table>
<tbody>
<tr>
<td>
<h2>Simulateur de calcul de paie</h2>
</td>
<td style="width: 20px">
<img id="loading" style="display: none" src="indicator.gif" />
</td>
<td>
<a id="lnkConnexion" href="javascript:connexion()">
| Connexion<br />
</a>
<a id="lnkFaireSimulation" href="javascript:faireSimulation()">
| Faire la simulation<br />
</a>
<a id="lnkEffacerSimulation" href="javascript:effacerSimulation()">
| Effacer la simulation<br />
</a>
<a id="lnkVoirSimulations" href="javascript:voirSimulations()">
| Voir les simulations<br />
</a>
<a id="lnkRetourFormulaire" href="javascript:retourFormulaire()">
| Retour au formulaire de simulation<br />
</a>
<a id="lnkEnregistrerSimulation" href="javascript:enregistrerSimulation()">
| Enregistrer la simulation<br />
</a>
<a id="lnkTerminerSession" href="javascript:terminerSession()">
| Terminer la session<br />
</a>
</td>
</tbody>
</table>
<hr />
<div id="content">
<table>
<tr>
<td>URL du simulateur</td>
<td><input type="text" id="urlServiceWeb" name="urlServiceWeb" size="80"></td>
</tr>
</table>
<div id="erreur">
<h3>Réponse du serveur :</h3>
<div id="erreur1"></div>
<div id="erreur2"></div>
</div>
</div>
</body>
</html>
Aşağıdaki eklemeleri yaptık:
- 27-29. satırlar: Simülasyon hizmetine bağlanmayı sağlamak için [Connexion] menü seçeneği eklendi;
- 55-56. satırlar: Simülatörün URL girişi;
- 59-63. satırlar: Bağlantı başarısız olursa bir hata alanı;
Kodun yeniden düzenlenmesi yalnızca yukarıdaki 14. satırdaki [myScripts.js] kodunda gerçekleştirilir. Başka hiçbir değişiklik yapılmaz. Kod şu şekilde değişir:
// belge yüklenirken
$(document).ready(function () {
// sayfanın çeşitli bileşenlerinin referansları alınır
loading = $("#loading");
content = $("#content");
erreur = $("#erreur");
erreur1 = $("#erreur1");
erreur2 = $("#erreur2");
// menü bağlantıları
lnkConnexion = $("#lnkConnexion");
lnkFaireSimulation = $("#lnkFaireSimulation");
lnkEffacerSimulation = $("#lnkEffacerSimulation");
lnkEnregistrerSimulation = $("#lnkEnregistrerSimulation");
lnkVoirSimulations = $("#lnkVoirSimulations");
lnkTerminerSession = $("#lnkTerminerSession");
lnkRetourFormulaire = $("#lnkRetourFormulaire");
// bunlar bir tabloya eklenir
options = [lnkConnexion, lnkFaireSimulation, lnkEffacerSimulation, lnkEnregistrerSimulation, lnkVoirSimulations, lnkTerminerSession, lnkRetourFormulaire];
// sayfanın bazı öğelerini gizlenir
loading.hide();
erreur.hide();
// menüyü sabitliyoruz
setMenu([lnkConnexion]);
});
- satır 6-8: [index.html] sayfasında bağlantı hatalarını gösteren alanın tanımlayıcıları;
- 10. satır: simülatöre bağlantı için yeni bağlantı;
- 21. satır: hata alanı başlangıçta gizlidir;
- 23. satır: Yalnızca bağlantı bağlantısı görüntülenir;
[index.html] sayfasında, bağlantı bağlantısı şu şekilde tanımlanmıştır:
<a id="lnkConnexion" href="javascript:connexion()">
| Connexion<br />
</a>
JS [connexion] işlevi (satır 1) şu şekildedir:
var urlServiceWeb;
var erreur, erreur1, erreur2;
function connexion() {
// web hizmetinden urlServiceWeb'i alıyoruz
urlServiceWeb = $("#urlServiceWeb").val();
// giriş formunu alıyoruz
$.ajax({
url: urlServiceWeb + '/Pam/Formulaire',
type: 'POST',
dataType: 'html',
beforeSend: function () {
// bekleme sinyali yanıyor
loading.show();
},
success: function (data) {
// sonuçların görüntülenmesi
content.html(data);
// menü
setMenu([lnkFaireSimulation]);
},
error: function (jqXHR) {
erreur2.html(jqXHR.responseText);
erreur1.html(jqXHR.getAllResponseHeaders().replace(/\r\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/\n/g, "<br/>"));
erreur.show();
},
complete: function () {
// bekleme sinyali kapalı
loading.hide();
}
});
}
- 7. satır: Kullanıcı tarafından girilen URL değeri alınır. Bu değer, 1. satırdaki genel değişkene atanır. Böylece dosyanın diğer işlevlerinde de bu değer kullanılabilir;
- 10. satır: Simülatörün URL ve [/Pam/Formulaire] değerlerine Ajax çağrısı yapılır. Bu URL değeri, simülasyon bilgilerinin (çalışanlar, çalışılan saatler, çalışılan günler) girilmesine ilişkin kısmi görünümü sağlar. [pam-web-02]'in ilk sürümünde, bu URL yeterliydi. Bu işlevin önüne, başlangıç sayfasını getiren URL otomatik olarak ekleniyordu. Artık, başlangıç sayfasının simülatörü barındıran sunucudan başka bir sunucu tarafından sağlanabileceği varsayılmaktadır. Bu durumda, URL ve [/Pam/Formulaire]'e, 1. satırdaki [urlServiceWeb] değişkeni eklenmelidir; bu, simülatörün URL'idir (örneğin, http://172.19.81.34/pam-web-02). Bu işlem, dosyadaki tüm Ajax çağrıları için yapılmalıdır;
- 17-22. satırlar: Bağlantı başarılı olursa, [Formulaire.cshtml] kısmi görünümü görüntülenir ve içinde yalnızca [Faire la simulation] bağlantısı bulunan bir menü görüntülenir (21. satır);
- 23-27. satırlar: Bağlantı başarısız olursa:
- 24. satırda, web sunucusu tarafından gönderilen HTML yanıtı görüntülenir (varsa);
- 25. satırda, web sunucusu tarafından gönderilen HTTP başlıkları görüntülenir (eğer yanıt vermişse);
Hepsi bu kadar. Başarılı olursa, aşağıdaki sayfa görüntülenir:
![]() |
Artık önceki durumdayız; artık sayfalar Ajax çağrılarıyla yükleniyor. Yukarıdaki örnekte, [Faire la simulation] bağlantısına tıklandığında, [myScripts.js] dosyasındaki aşağıdaki kod çalıştırılacaktır:
function faireSimulation() {
// referanslar alınır
var simulation = $("#simulation");
var formulaire = $("#formulaire");
// form geçerli mi?
var formValid = formulaire.validate().form();
if (!formValid) return;
// Ajax çağrısı manuel olarak yapılıyor
$.ajax({
url: urlServiceWeb + '/Pam/FaireSimulation',
type: 'POST',
data: formulaire.serialize(),
dataType: 'html',
...
});
// menü
setMenu([lnkEffacerSimulation, lnkEnregistrerSimulation, lnkTerminerSession, lnkVoirSimulations]);
}
- Tek bir değişiklik yapıldı; 10. satırda, önceki URL ifadesinin önüne artık simülatörün ön eki eklenmiştir;
9.27.3. Yeniden yapılandırılmış projenin testi
9.26 numaralı paragrafta, [pam-web-02] uygulamasının Ultidev sunucusuna nasıl kurulacağını göstermiştik. Buradan devam edeceğiz:
![]() |
- [6] adresinden [bootstrap/index.html] sayfasının görüntülenmesini talep ediyoruz. Aşağıdaki görünüm elde edilir:
![]() |
Hatalı bir URL adresi girelim:
![]() |
- [10]'te, sunucunun yanıtındaki HTTP başlıkları;
- [11]'e, sunucunun yanıtındaki HTML belgesini;
Doğru URL yazılırsa:
![]() |
şu yanıtı alırız:
![]() |
9.27.4. Android ikili dosyasının oluşturulması
Az önce oluşturduğumuz ve test ettiğimiz statik web sitesinden Android ikili dosyasını oluşturacağız: [1] :
![]() | ![]() |
[2] dosyasına, Android ikili dosyasını oluşturacak olan [Phonegap] eklentisini yapılandırmak için kullanılacak bir [config.xml] dosyası ekliyoruz. Kod şu şekildedir:
<?xml version='1.0' encoding='utf-8'?>
<widget id="android.exemples.pam" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Pam</name>
<description>
IstiA - Université d'Angers
</description>
<author email="serge.tahe@univ-angers.fr">
Serge Tahé
</author>
<content src="index.html" />
<access origin="*" />
<allow-navigation href="*" />
<allow-intent href="*" />
<plugin name="cordova-plugin-whitelist" />
</widget>
- 7-9. satırlar: buraya iletişim bilgilerinizi girin;
- 11-13. satırlar: Bu satırlar, Android cihazında çalışacak olan web uygulamasına gömülü JavaScript'in, bu cihazın dışındaki URL dosyalarına istek göndermesini sağlar;
[Content/bootstrap] klasörünün içeriğini zip dosyasına sıkıştırıyoruz:
![]() |
Ardından Phonegap sitesine gidiyoruz: [http://build.phonegap.com/apps]:
![]() |
- [1]'ten önce, bir hesap oluşturmanız gerekebilir;
- [1]'te, başlıyoruz;
- [2]'te, yalnızca bir Phonegap uygulamasına izin veren ücretsiz bir plan seçin;
- [3]'te, [4] adlı sıkıştırılmış uygulamayı indirin;
![]() |
![]() |
- [5]'te, uygulamaya bir isim verin;
- [6] bağlantısına tıklayarak OS, IoS, Android ve Windows için ikili dosyaları derleyin. Bu işlem birkaç saniye sürebilir;
![]() |
- [7-9]'te, Android ikili dosyasını indirin;
![]() |
Android tablet için bir emülatör başlatın (bkz. paragraf 11.1):
![]() |
Yukarıda, Android 21 sürümündeki API ile bir tablet emülatörü başlatılıyor. Emülatör başlatıldıktan sonra,
- yan taraftaki kilidi (varsa) sürükleyip bırakarak kilidi açın;
- fare ile indirdiğiniz [Pam-debug.apk] dosyasını sürükleyip emülatörün üzerine bırakın. Dosya bu şekilde yüklenecek ve çalıştırılacaktır;
![]() |
9.27.3 numaralı paragrafta açıklandığı gibi, simülatörün URL dosyasını [1] olarak ayarlayın. Bunu yaptıktan sonra, [2] bağlantısı ile simülatöre bağlanın:
![]() |
Uygulamayı emülatörde test edin. Çalışması gerekir.








































































































































































































