Egosuz Programlama & Programcı Egosu

Biz developer'lar bazen yaptığımız iş gereği "dünyayı kurtarıyoruz" havasına girebiliyoruz (aslında sadece developer'lar değil test müh, analistler, tasarımcı arkadaşlar vs.). Sektörde her zaman bu gibi arkadaşlar hep varlar ve dehşet-ül vahşet düzeyde bir özgüven-ego patlaması yaşayabiliyorlar.Bu arkadaşlar istisnai olmadığı taktirde %99 oranında hep yazdıkları kodların veya ortaya attıkları tezlerin en iyisi olduğunu iddia ederler ve genelde "abi bundan daha iyi bir çözüm yok yaa.." gibi cümlelerle tanırız bu arkadaşları. Neden bu şekilde iddialı cümleler kurduklarını anlamak için bence biraz gerilere gidip bu arkadaşların staj dönemlerine inmek gerekir zira bu arkadaşlar çok büyük olasılıkla sağlam bir code review'lık dönemini kaçırmış olabilirler veya junior'lık dönemlerinde kodları inşaata kaçmış olabilir :)

Gereksiz ve anlamsızca ego sahibi olan bu arkadaşlar her şey hakkında en iyi ve en kabul görebilir öneriler sunduklarını düşünürler, her ortamda teknik bilgilerini değişik bir jargonla anlatarak ortaya koymaya çalışırlar, en iyi kodu onlar yazmıştır ve eleştiriye kapalıdırlar. Bazen öyle durumlar olur ki bir konu hakkında konuşuluyordur ve hiçbir fikri olmamasına rağmen sırf o konu hakkında expert olduğunu kanıtlamaya çalışmak adına tuhaf bir şekilde konuşmaya başlarlar. Tabiki de benim fikrim ancak bu arkadaşlarla kavgasız gürültüsüz iletişim kurabilmenin en iyi yolu bu tarz hareketler yaptıklarını anladığınızda öyle bir şey söyleyin ki bir daha benzeri hareketler yapmaya kalkışmasın. Bu tabii ki de küfretmek falan değil :) Ortaya koydukları tez'in sırf söylemiş olmak için söylediklerini düşünüyorsanız ve siz daha doğru bir tez'e sahipseniz bu tezinizi savunmaktan kesinlikle çekinmeyin ve onun tezini çürütmeye çalışın.

Böyle bir kişi eğer ekip arkadaşınızsa yani aynı projede beraber development yapıyorsanız şu gibi cümleler duymanız hiç zor değil "abi burayı böyle yazmasak daha iyi yaa..", "abi ben daha önce bunla ilgili çok kral bi çözüm bulmuştum yaa..","bu böylemi yapılır yaa.." falanlar filanlar. Bu gibi cümleler kuran bir ekip arkadaşınız var ise ve yüksek oranda ego sahibi olduğunu düşünüyorsanız benzer cümleler kurmasının asıl amacı üstünlük sağlamaya çalışmak veya benzer bir kodu oda yazmıştır zamanında ve o yazdığının ilelebet en iyi çözüm olacağını düşünür çünkü ezberlemiştir onun için hep bildiği yoldan gitmek gerekmektedir ve senide ısrarla o yola sokmaya çalışır.

Evet malesef bende bu arkadaşlardan biriyle bir süreliğine çalışmak zorunda kaldım ve yaşadığım bir örneği vermeden geçemicem. Eski çalıştığım şirkette bir mobil projesini geliştirme yapan arkadaştan devraldım ve geliştirmeye ben devam edecektim kendisi başka bir  departmana geçmişti. Projeyi devraldığımda markette bulunan versiyonu yanılmıyorsam 1.1 di ve ben yaklaşık 2 sene o projede çalışmıştım. 2 sene dolmasına yakın bir dün bu arkadaş burnu 5 karış kalkık vaziyette yanıma geldi ve ne hikmettir bilmem proje hakkında sorular sormaya başladı.Sorulardan biride uygulamanın bir sonraki versiyonunun ne olacağıydı. Bende 3.0 olacağını söyledim.O arkadaş sahip olduğun egoyla şöyle bir cümle kullandı "Ben sana bişey söylimmi siz bu versiyonlamayı kesin yanlış yapıyorsunuz..." bende dedim ki "wtf..? yani ne alaka nerden çıkardın bunu ?" oda "ben bu projeyi bıraktığımda v1.1 di.." arkadaş zannediyordu ki kendisi uygulamanın %25 inin geliştirmesini yapıp v1.1 ile markete çıkıp uygulama bitmişti. Geriye kalan 2 sene içerisinde uygulamanın %85'e yakının geliştirilip 6 defa update çıkıldığından haberi yok, haberi olmasa daha düşünmesine de gerek yok çünkü 2 sene önce o uygulamayı yaptı ve bitti. Ben naptım peki ? Bir güzel o kişiye konuyu açıklayıp arkasını dönüp fıtı fıtı gidişini izleyerek çayımı yudumladım.

Malesef ego sahibi insanlar IT sektöründe hep olacaktır, bu insanlar yarın bir gün bizlerde olabiliriz ama sahip olduğumuz ego'nun makul düzeyde olması çok önemli.

Peki gerçekten biz developer'lar makul düzeyde ego'ya nasıl sahip olabiliriz, neler yapmalıyız ..? Bununla ilgili bilgisayar bilimleri için bir çok makale yayınlamış olan yazar Gerard M. Weinberg "Egoless Programming" adında bir makale yazıyor. Üstad konu ile ilgili yaklaşık 40 yıl önce 10 tane kural tanımlamış. Bunlar;

1. Understand and accept that you will make mistakes

Türkçesi "herkes gibi sende hata yapacaksın arkadaş bunu anla ve kabul et.." diyor. Bug'ı olmayan uygulama olmaz ve her zaman uygulamalarda exception'lar meydana gelecektir. Önemli olan bu gibi durumlar production'ı etkilemeden yakalayıp müdahale edip uygulamamızı hatalardan arındırmaktır. Tabi bug'ları ortaya çıkartmak içinde çok iyi test süreçleri gerekmektedir.

 

2. You are not your code

Review yapmanın asıl amacının meydana gelmiş veya gelecek olan problemleri bulmak olduğun hatırla ve problemler olacaktır da. Review sırasında birisi projenizde herhangi bir problem bulduğunda veya eleştirel bir yorumda bulunduğunda bunu kişisel bir şey olarak algılama ve sadece geliştirme yapmaya devam et.

 

3. No matter how much you know, someone else will always know more

Hiç kimse ben iyi programcıyım dememelidir. Her zaman için herkesin farklı bakış açısından sorunları analiz edip çözümler ürettiğini düşünmek gerekir ve buda her zaman için daha iyisinin olabileceği gerçeğini unutmamamız gerektiğini işaret eder. Sahip olduğumuz bilgi ve beceriler tabiki çok değerli ancak bunun da bir limitinin olduğunu bilmemiz gerekir. Geliştirme yaparken her developer'ın izlediği farklı bir yol olabilir ve daha önce senin hiç bilmediğin-denemediğin bir şekilde sorunları çözüyor olabilir ve bu gibi case'leri çok fazla abartıp sidik yarışına girmeye gerek yoktur. Sadece şunu bilmek gerekir her zaman senden daha farklı veya senden daha iyisini bilen biri olacaktır.

 

4. Don’t rewrite code without consultation

Bu kural bize kodu fix etme ile yeniden yazma arasında ince bir çizgi olduğundan bahsediyor. Bu farkı iyi bir şekilde bil ve değişiklik yapacağın yerlerde core koda dokunup projeyi yeniden geliştirmene neden olacak şeylerden uzak dur.

 

5. Treat people who know less than you with respect, deference, and patience

Ekibinde veya etrafında her zaman senin kadar teknik bilgisi olmayan insanlar olacaktır. Bunlara karşı anlayışlı ve saygılı olmaya çalış. Eğer mümkünse onlara konudan bahset ve teknik tarafını anlamada yardımcı ol. 

 

6. The only constant in the world is change

Değişim yazılım dünyasında kaçınılmazdır ve değişime açık ol ve geldiğinde gülümseyerek kabul et :) İstenilen değişikliği bir challenge olarak gör ve kendine yeni bir şeyler katacağını düşünerekten hareket et.

 

7. The only true authority stems from knowledge, not from position

Belli konular hakkında bilgi sahibi olmak beraberinde yetkili olmayı da gerektirir ve yetkide beraberinde saygılı olmayı getirir. Eğer egosuz bir ortamda saygı istiyorsan öncelikle bilgi üretmen gerekir.

 

8. Fight for what you believe, but gracefully accept defeat

Bazen sahip olduğunuz fikirlerin veya ortaya attığınız tezlerin reddedilebileceği gerçeğini unutmayın bu her zaman ihtimal dahilindedir. Bazen haklı olduğun durumlarda bile asla intikam almaya çalışmayın hatta "ben sana demiştim.." vs gibi şeyler dahi söylememeye özen gösterin.

 

9. Don’t be the “coder in the corner”

Ofisin en karanlık köşesinde oturup mesai saati boyunca sadece kişisel ihtiyaçlarını karşılamak için yerinden kalkan kişi olmayın çünkü o developer gözden uzak olduğu gibi gönülden de ırak olur. Her zaman için ofis içinde olup bitenlerden haberiniz olsun ve her zaman kendinizi ekibinizin ve şirketinizin bir parçası olarak görün.

 

10. Critique code instead of people – be kind to the coder, not the code

Mümkün olduğunca yazacağın yorum satırlarında pozitif bir dil kullanmaya çalış ve yorumlarında dünyaları yeniden yarattığından çok kod kalitesinin gelişimine yönelik açıklamalarda bulun. Yorum standartlarınız standart olup resmi bir dil kullanmaya özen gösterin ve her zaman için projeye sizden sonra gelecek olan kişiyi düşünün.

Yazının başında da bahsettiğim gibi ego makul düzeyde olduğu sürece güzeldir. Yaptığımız işlerle övünürken başkalarını ezme düşüncesine girmemeliyiz ve bırakın başkaları bizi övsün. Böyle olduğu taktirde çalışma hayatı hem siz hemde çalışma arkadaşlarınız için daha eğlenceli ve çekilesi olacaktır.

 

Liskov Substitution Principle

Geldik Liskov prensibine. Bu yazıda SOLID' in "L" si olan "Liskov Substitution Principle (LSP)" prensibinden bahsedicez. 

LSP'nin tarihine bakacak olursak; bu prensip ilk olarak 1987 yılında Barbara Liskov tarafından tanıtıldı ve daha sonrasında Jeannette Wing ile birlikte 1993 yılında bu prensibi resmi olarak yayınladılar.

Bu prensibin orjinal tanımı "Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T." dır. Ancak bu tanımdan senin benim gibi hiç kimse bir şey anlamadığı için namı değer bob amca Robert C. Martin  "Agile Principles, Patterns, and Practices in C#" kitabında bu prensibi kısaca "Subtypes must be substitutable for their base types." diye tanımladı ve günümüze yazılım dünyasında da bu tanımıyla kabul görmektedir.

Bu kadar tarih ve ingilizce bilgisinden sonra gelelim Liskov'un Türkçe sine. Bu prensip bize şunu söyler ; Alt sınıflardan oluşturulan nesneler üst sınıfların nesneleriyle yer değiştirdiklerinde aynı davranışı göstermek zorundadırlar.. Daha bizim dilden konuşacak olursak ; alt sınıf üst sınıftan kalıtım alırken property, method vs gibi bazı kullanmayacağı özellikler kazanmamalıdır. Eğer böyle bir duruma sebep olacak bir inheritance işlemi var ise alt sınıfın kullanmadığı fonksyon, metod vs. için farklı bir çözüm (sadece ihtiyacı olan özelliklerin bulunduğu başka bir base class vs. gibi) düşünülmelidir çünkü alt sınıf üst sınıfın bütün özelliklerini tam anlamıyla kullanamadığından yer değiştirdiklerinde aynı davranışı gösteremeyeceklerdir ve Liskov'a ters düşen bir durumdur.

 

Örnek uygulama üzerinden ilerleyelim, case şu şekilde olsun ; Bir tane Bank isminde bir class ve bu class içersinde Withdraw adında ara çekm işlemini yapan bir virtual metod olsun, 2 tanede hesap tanımı yapalım. Bank objesini kullanarak bu hesaplardan para çekme işlemini yapalım ve tabi ki bunu LSP kurallarına uyarak yapmaya çalışalım.  

 

  public class Bank
    {
        public void Withdraw(Account acc, int amount)
        {
            acc.Withdraw(acc.Id, amount);
        }
    }

    public class Account
    {
        public Account(int AccountId)
        {
            this.Id = AccountId;
        }

        public virtual int Id { get; set; }

        public virtual void Withdraw(int accountId, int amount)
        {
            Console.WriteLine("In base withdraw");
        }
    }

    public class SavingAccount : Account
    {
        public SavingAccount(int savingAccountId) : base(savingAccountId)
        {

        }

        public override void Withdraw(int accountId, int amount)
        {
            Console.WriteLine("In SavingAccount withdraw");
        }
    }

    public class CurrentAccount : Account
    {
        public CurrentAccount (int currentAccountId) : base(currentAccountId)
        {

        }

        public override void Withdraw(int accountId, int amount)
        {
            Console.WriteLine("In CurrentAccount withdraw");
        }
    }

 Şimdide Main fonksiyonu içerisinde bu yazmış olduğumuz class'ları kullanarak para çekme işini yapalım.

 using System;

    public class Program
    {
        static void Main(string[] args)
        {
            Bank bank = new Bank();
            Account acc = new Account(1);
            bank.Withdraw(acc, 100);
        }
    }

Yukarıda ki örnekte olduğu gibi Bank class'ı içerisinde bulunan Withdraw metodu bir Account objesini parametre olarak alıyordu. İlk kullanımda bir adet acc isminde Account oluşturup Withdraw metoduna verdik ve acc de bulunan Withdraw metodunu kullanarak para çekme işlemini yaptık.

Ancak LSP bize ne diyordu ; Alt sınıflardan oluşturulan nesneler üst sınıfların nesneleriyle yer değiştirdiklerinde aynı davranışı göstermek zorundadırlar. Aşağıdaki örnekte de LSP ye uygun biçimde kullanımı göreceğiz

 using System;

    public class Program
    {
        static void Main(string[] args)
        {
            Bank bank = new Bank();

            Account saving = new SavingAccount(2); //LSP nin dediği gibi sub class'lar base class'larla yer değiştirdiklerinde aynı davranışı göstermeleri beklenir
            bank.Withdraw(saving, 100);
        }
    }

Yukarıda ki gibi ise saving  isminde bir SavingAccount oluşturduk ve bu objede Account'ın bir alt class'ı olduğu için onun yerine kullanılabilir durumda Account saving = new SavingAccount(2); dedik ve Bank class'ında bulunan Withdraw metoduna parametre olarak geçtik ve aynı ilk kullanımda yaptığımız gibi saving objesininde Withdraw metodunu çağırıp para çekme işlemini tamamladık.

 

İnternette Liskov için çoğunlukla Rectangle örneğini görmek mümkün, bende herkes onu yazmışken biraz daha farklı bir örnek ile ele alim istedim ve bu örnek üzerinden gittim umarım anlaşılır olmuştur :)

Open Closed Principle

SOLID prensipleri yazı serisinde daha öncesnde SOLID nedir ve Single Responsibility konularından bahsetmiştik. Şimdi ise sırada SOLID'in  "O" su olan Open-Closed Principle. Bu prensip 1988 yılında fransız akademist Bertrand Meyer tarafından ortaya atılıp şu şekilde tanımlandı;

"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

Peki ne demek bu "gelişime açık değişime kapalı" sözü ? Şu bir gerçek ki bir kod yazacaksınız ve ilelebet hep aynı kalacak.. tamamen palavra. Hiç bir kod yıllar içinde değişime uğramadan kalamaz, sürekli olarak yeni bir feature ekleme durumu söz konusudur ve müşteri sürekli olarak "şunu da ekleyelim, bunu da ekleyelim, şu da olsun.." vs. vs. istekler hiçbir zaman bitmeyecektir. Bu tarz durumlar için Open-Closed bize yeni gelen değişiklik isteklerini projenize eklerken yazmış olduğunuz sınıfların genel davranışlarını değiştirmeden sadece birkaç ufak dokunuşla bu geliştirmeleri ekleyebileceğiniz class lar yazmamızı söylüyor. Yani Core koda gerekirse hiç müdahale etmeden yeni feature'ları ekleyin diyor. Sebebi ise mevcut koda yapılacak olan her müdahale yeni bug'ların çıkmasına neden olabilir ve buda yeniden test-analiz süreci demektir ki bunu hiçbir firma veya çalışan istemez. 

Örnek bir proje üzerinden ilerleyelim. Bad Example ve Better Example diye Open-Closed'u iki şekilde ele alalım.

 

Bad Example 

Örneğimiz şu şekilde olacak; Araba ve Otobüs üretimi yapan bir fabrikamız var diyelim. Bunun için bir tane aşağıdaki gibi Vehicle adında bir class'ımız olsun. Bu class içerisinde VehicleType isminde bir enum ve bu enum'ında Car-Bus diye 2 değeri olsun. Daha sonra Vehicle class'ından inherit olmuş Car ve Bus isminde 2 class oluşturalım ve constructor'larında gerekli enum değerlerini verelim.

Vehicle.cs

namespace BadExample
{
    public class Vehicle
    {
        public VehicleType VType { get; set; }
    }

    public enum VehicleType
    {
        Car,
        Bus
    }

    public class Car : Vehicle
    {
        public Car()
        {
            this.VType = VehicleType.Car;
        }
    }

    public class Bus : Vehicle
    {
        public Bus()
        {
            this.VType = VehicleType.Bus;
        }
    }
}

 

Şimdi ise bu araçları üretme kısmına geldi. VehicleFactory adında bir class oluşturalım ve bu class içerisinde ProduceVehicle isminde Vehicle  objesini parametre olarak alan bir metod olsun ve bu metoda verilen objedeki vehicleType enum değerine göre ilgili aracı üreten fonksiyonları çağırsın.

VehicleFactory.cs

namespace BadExample
{
    public class VehicleFactory
    {
        public void ProduceVehicle(Vehicle vehicle)
        {
            switch (vehicle.VType)
            {
                case VehicleType.Car:
                    ProduceCar((Car)vehicle);
                    break;
                case VehicleType.Bus:
                    ProduceBus((Bus)vehicle);
                    break;
            }
        }

        private void ProduceCar(Car car)
        {
            Console.WriteLine("Car Produced\n");
        }

        private void ProduceBus(Bus car)
        {
            Console.WriteLine("Bus Produced\n");
        }
    }
}

 

Şimdi sırada yazmış olduğumuz class'ları kullanarak araçları üretme işlemi var. Bunun için Program.cs class'ı aşağıdaki gibi olacak

Program.cs

namespace BadExample
{
    class Program
    {
        static void Main(string[] args)
        {
            VehicleFactory vf1 = new VehicleFactory();
            vf1.ProduceVehicle(new Car());
 
            VehicleFactory vf2 = new VehicleFactory();
            vf2.ProduceVehicle(new Bus());

            Console.ReadLine();
        }
    }
}

 Uygulamamızın Ekran çıktısı

 

Ne güzel araçlarımız ürettik kodumuz tıkır tıkır çalışıyor ve fabrika haftada 300 car-bus üretiyor. Peki patron çıldırdı dedi ki "Arkadaşlar 2 ay sonra kamyon üretmeye başlıyoruz, bütün altyapıyı hazır edin..". Hiç sorun değil nasıl olsa kodumuzu yazdık arabayı otobüsü nasıl ürettiysek kamyonu da öyle üretiriz deyip kamyon için aşağıdaki gibi kodlarımızı modify edip değişiklikleri yapalım.

1- İlk olarak => VehicleType enum'ına Truck diye yeni bir alan eklemeliyiz

 public enum VehicleType
    {
        Car,
        Bus,
        Truck//Truck enum değerini ekledik
    }

 

2- İkinci olarak => Vehicle class'ına Truck objesini oluşturma. Nasıl Car ve Bus için Vehicle'dan inherit olan class lar yazdık aynı şekilde Truck içinde bunu yapıyoruz

namespace BadExample
{
    public class Vehicle
    {
        public VehicleType VType { get; set; }
    }

    public enum VehicleType
    {
        Car,
        Bus,
        Truck
    }

    public class Car : Vehicle
    {
        public Car()
        {
            this.VType = VehicleType.Car;
        }
    }
    public class Bus : Vehicle
    {
        public Bus()
        {
            this.VType = VehicleType.Bus;
        }
    }

    public class Truck : Vehicle//Truck objesini tanımladık 
    {
        public Truck()
        {
            this.VType = VehicleType.Truck;
        }
    }
}

 

3- Üçüncü olarak =>  VehicleFactory class'ına  ProduceTruck class'ını ekleyip ProduceVehicle metoduna Truck için gerekli kontrolleri ekleyeceğiz

using System;

namespace BadExample
{
    public class VehicleFactory
    {
        public void ProduceVehicle(Vehicle vehicle)
        {
            switch (vehicle.VType)
            {
                case VehicleType.Car:
                    ProduceCar((Car)vehicle);
                    break;
                case VehicleType.Bus:
                    ProduceBus((Bus)vehicle);
                    break;
                case VehicleType.Truck://truck üretimi için logic kısmını yazdık
                    ProduceTruck((Truck)vehicle);
                    break;
            }
        }

        private void ProduceCar(Car car)
        {
            Console.WriteLine("Car Produced\n");
        }

        private void ProduceBus(Bus car)
        {
            Console.WriteLine("Bus Produced\n");
        }

        private void ProduceTruck(Truck truck) //truck üretimi yapan metodu ekledik
        {
            Console.WriteLine("Truck Produced\n");
        }
    }
}

 

4- Dördüncü olarak =>  Yazmış olduğumuz kodları kullanma vakti, Main fonksiyonda da Truck üretimi için gerekli metod çağrılarını yapacağız.

namespace BadExample
{
    class Program
    {
        static void Main(string[] args)
        {
            VehicleFactory vf1 = new VehicleFactory();
            vf1.ProduceVehicle(new Car());
            
            VehicleFactory vf2 = new VehicleFactory();
            vf2.ProduceVehicle(new Bus());
            
            VehicleFactory vf3 = new VehicleFactory();
            vf3.ProduceVehicle(new Truck());
            
            Console.ReadLine();
        }
    }
}

Uygulamamızın Ekran çıktısı

 

Kamyonumuzu ürettikkk. Patron mutlu alan mutlu satan mutlu.. Peki ama yeni bir araç yani Kamyon türünde bir Vehicle üretmek için neler yaptık öyle... Elimizi atmadığımız class kalmadı. Bu işlemi tam 4 farklı adımda halledebildik. Bu peki istediğimiz bir şey mi ?.. Ne diyordu Open-Closed prensibi "gelişime açık değişime kapalı" Peki biz ne yaptık; projede ki logic kısmı dahil her yere müdahale edip bir takım değişiklikler yaptık yani prensibe uygun hareket etmedik.

Peki nasıl olması gerekirdi ? Sadece Main fonksiyonunda bir parça kod ekleyip sorunu çözecek halimiz yok tabiki de ancak patron yarın tekrardan gelip "Motorsiklet üretmeye başlıyoruz, Bisiklet üretmeye başlıyoruz.." diye devam etme ihtimaline karşı daha generic ve core koda yani logic'in bulunduğu kod kısımlarına çok fazla dokunmadan bir kaç extended class yazarak yeni üretim işlemine başlıyor olmamız gerekirdi.

 

Better Example

Şimdi gelin Truck üretim işlemimizi biraz daha "Better than the previous example" sloganıyla yazalım 

İlk olarak Vehicle.cs ile başlayalım. Bu sefer Vehicle class'ımız abstract içerisinde bir adet abstract olarak tanımlı Produce() metodu var ve Car, Bus ve Truck objeleri de yine Vehicle class'ını implemente edip ve Produce metodunu kullanacaklar.

using System;

namespace GoodExample.MyFolder
{
    public abstract class Vehicle
    {
        public abstract void Produce();
    }

    public class Car : Vehicle
    {
        public override void Produce()
        {
            Console.WriteLine("Car Produced\n");
        }
    }
    
    public class Bus : Vehicle
    {
        public override void Produce()
        {
            Console.WriteLine("Bus Produced\n");
        }
    }

    public class Truck : Vehicle
    {
        public override void Produce()
        {
            Console.WriteLine("Truck Produced\n");
        }
    }
}

 

VehicleFactory.cs class'ımızda artık hiçbir if/else yada switch/case condition'ı bulunmicak. Bu class ta yine Vehicle tipinde parametre alan ProduceVehicle() adında bir metod olacak ve tek görevi parametre olarak aldığı abstract Vehicle'dan türeyen objeyi alıp Produce() metodunu çağırmak. 

namespace GoodExample.MyFolder
{
    public class VehicleFactory
    {
        public void ProduceVehicle(Vehicle vehicle)
        {
            vehicle.Produce();
        }
    }
}

 

Program.cs'ın diğer örnekte olduğu gibi aynısını kullanıcaz yani üretmek istediğimiz objenin instance'ını alıp üretime başlicaz.

using System;

namespace GoodExample
{
    class Program
    {
        static void Main(string[] args)
        {
            VehicleFactory vf1 = new VehicleFactory();
            vf1.ProduceVehicle(new Car());

            VehicleFactory vf2 = new VehicleFactory();
            vf2.ProduceVehicle(new Bus());

            VehicleFactory vf3 = new VehicleFactory();
            vf3.ProduceVehicle(new Truck());
            
            Console.ReadLine();
        }
    }
}

Uygulamamızın Ekran çıktısı

 

Şimdi içimizden şu soruyu sorabiliriz.. "Eeee noldu şimdi aynı işi yaptık, hedefimiz Truck üretimi yapmaktı Bad Example'da da yaptık Better Example'da da..". Cevabı açık ve net arkadaşlar 

  • Projemizi daha generic hale getirdip hiçbir condition'a bağımlı tutmadık,
  • Abstraction kullanarak projedeki bazı yerleri daha kolay geliştirebilir hale getirdik,
  • Core taraftaki kodu gelişime açık çok büyük köklü değişimlere kapalı hale getirdik,
  • Yeni bir üretim işlemi  başlasın Vehicle objesinden inherit olan class'ımızı olluşturup Produce metodunun içerisini doldurduktan sonra kolayca üretime başlayabilecek hale geldik,
  • Yani kısaca projemizi Open-Closed prensibine uygun hale getirdik

BadExample'da yaptığımız gibi yeni bir araç üretimine başlamak için yapmış olduğumuz o 4-5 farklı değişiklikten kurtulup BetterExample'da olduğu gibi sadece üretilecek olan nesneyi Vehicle objesinden inherit edilmiş şekilde tanımlayıp Main fonksiyonunda Üretim işlemini başlatmak yeterli olacaktır.

Hadi son olarak Motorcycle üretelim.  İlk olarak nesnemizi tanımlıyoruz 

    public class Motorcycle : Vehicle
    {
        public override void Produce()
        {
            Console.WriteLine("Motorcycle Produced\n");
        }
    }

Sonrasında Main fonksiyonunda üretime başla diyoruz

using System;

namespace GoodExample
{
    class Program
    {
        static void Main(string[] args)
        {
            VehicleFactory vf1 = new VehicleFactory();
            vf1.ProduceVehicle(new Motorcycle());


            Console.ReadLine();
        }
    }
}

 

That's it :)

 

Web API İçin 5 Altın Kural

Çoğu zaman yazılım geliştirirken ortaya çıkan ürün yazılımcılar için değilde son kullanıcılar içindir ve bu kişiler teknik olmayan ve sadece UI tarafıyla haşır neşir olan kişilerdir. Ortaya çıkan ürünün iyi bir arayüzü vardır ve son kullanıcı arkada çalışan hangi teknolojiymiş, nasıl çalışıyormuş gibi bilgilerle hiç ilgilenmezler.

Ancak WebApi için bunu söyleyemeyiz. Yazılımcı olarak geliştirmiş olduğunu WebApi projesi end-user için değil bazen kim olduğunu bilmediğimiz başka yazılımcılar içindir. Bu kişiler teknik bilgisi en azından junior developer seviyesinde olan kişilerdir ve ortaya çıkan ürünün interface'i değilde yazılım tarafında ki her bir ince detayı inceleyip kullanacak olan kişilerdir ve sizi eleştirme hakkına da sahiptirler. 

Api'ı kullanacak olan kişiler yazılımcılar olduğundan geliştirmeyi yaparken bir WebApi client'ıymış bakış açısıyla yaklaşıp öyle geliştirme yapmak gerekir ve aslında bir nevi bu bakış açısını da Api'ı kullanan kişilerle de paylaşmış oluyoruz.

Api geliştiriciler genelde geliştirme sırasında şu sorulara odaklanırken ;"Bu servisin ne yapması gerekiyor ?", "Bu servis metodu ne sağlamalı ?", "Content-type json mı, xml mi ?" etc. , Api'ı kullanan kişiler "En az efor sarf ederek bu Api'ı nasıl kullanırım ? etc".

Görüldüğü üzre Api'ı yazan ve Api'ı kullanan kişiler tamamiyle farklı şeylere odaklanıyorlar çünkü farklı bakış açılarıyla Api'ı kullanıyorlar.  Sonuç olarak Api'ı yazan kişiler olarak uygulamayı geliştirirken consume edecek kişilerin yani Api kullanıcılarının bakış açısından bakıp onlar bu APi'ı kullanırken ne gibi sorular sorabilir veya hangi sorunun cevabına en kolay şekilde ulaşabilirler bunu düşünerek hareket etmemiz gerekir.

Peki iyi güzel ama böyle bir Api geliştirmek için neler yapmak gerekir ? Bunun için birçok geliştirici tarafından standart kabul edilen 5 altın kural vardır;

  1. Documentation (Dökümantasyon)
  2. Stability and Consistency (Kararlılık ve Tutarlılık)
  3. Flexibility (Esneklik)
  4. Security (Güvenlik)
  5. User Acceptance (Kullanıcılar tarafından kabul görmesi)

Rule 1: Documentation 

Yazılım geliştiren kişilerin ortak pek sevmediği şeylerden biri sayfalarca analistler tarafından yazılmış dökümanları okumaktır. Sizde sevmiyorsunuz dimi ?.. :)  

İyi bir dökümantasyon Api için olmazsa olmazlardandır ve çoğu zaman Api'ları geliştirren kişiler olarak bu iş biz developer'lara bırakılmıştır. Kullanıcı tarafından düşünelim bir bankada çalışıyorsunuz ve banka radikal bir karar alarak bazı servislerini dışarıya açma kararı aldı ve başladınız Api metodlarını yazmaya. Api'lar prod'a alınıp kullanıma sunulduktan sonra kullanmak isteyen kişilerin ilk başlayacakları yer Api ile ilgili yazılan dökümanları okumaktır.  Yani birinin sizin Api'ınızı kullanmasını istiyorsanız ilk yapmanız geren şey dökümantasyon yazmaktır çünkü kullanıcıların ilk bakacakları şey budur.

Peki iyi bir dökümantasyonu nasıl yazarız ?

Yazılımla projeleri ile ilgili dökümanlarda genelde; hangi sırasıyla metodların çağrılacağı, metod parametreleri, değişken tipleri, request, response class'ları etc. ve dökümantasyon oluşturmak içinde bir çok tool'da bulunmaktadır aynı zamanda kendimizde bunları basit bir template belirleyerek yazabiliriz. 

Ancak harika yazılmış bir dökümanı üstte bahsettiğimiz yaygın olarak kullanılan döküman yazma tekniklerinden ne ayırır diye soracak olursa Api'ın kullanım örnekleri ve iyi bir eğitim içeriği. Bu ikisi Api'ı kullanan kişiler için en önemli iki şeydir diyebiliriz çünkü son kullanıcı için gerçek bir örnek üzerinden ve takıldığı noktaları eğitim içeriğine bakarak en hızlı ve kolay şekilde öğrenecektir. Ancak yazdığımız dökümanın şeması ve yazdığımız örnek kodların karmaşık olmamasına da dikkat etmeliyiz. Döküman yazıyoruz diye kullanıcıya 300 sayfalık bir roman veya 15 class'lık bir örnek projede yazmamalıyız :)

Dökümantasyon işiniz bittikten sonra yapabiliyorsanız yapmış olduğunuz örnek Api kullanım projelerini ve dökümanları çevrenizdeki developer'lara gönderin ve feedback'ler almaya çalışın. Gelen feedback'ler doğrultusunda da yazmış olduğunuz dökümanıdaha user-friend hale getirebilirsiniz.  

Yaptıkları dökümanları harika bulduğum birkaç firma var ki bunlar kendi sektörlerinde en iyiler bile değiller ancak gerçek bir Api dökümanı nasıl olmalı sorusuna verilecek en iyi cevabı vermiş firmalar;

 

Rule 2: Stability and Consistency

Daha önce Facebookûn Api'larını kullandıysanız ne sıklıkla Api'ları değiştirdiklerini bilirsiniz. Sürekli olarak bir güncelleme yayınlıyorlar ve çalışan birçok uygulama da patlıyor çatlıyor. Facebook'un ne kadar başarılı bir ürün olduğuna herkes hemfikirdir ancak başarılı olmalarının sebebi son derece iyi bir Api'a sahip oldukları değil milyarları geçen kullanıcı sayısı sahip olmaları ancak iş Api konusuna gelince developer'lar için bazen hayatı zindan ettikleri oluyor. Muhtemelen sizlerin geliştirdiği Api'lar milyarlarca kullanıcı tarafından kullanılmicak ancak o bakış açısıyla geliştiriyor olmakta fayda var. Yeni sürüm yayınladığınızda eski versiyonlara desteği en azından birkaç yıl daha sürdürüyor olmak gerekir en azından mevcut kullanıcıları en iyi şekilde yeni versiyondan haberdar ederek onların yeni versiyona geçmelerini sağlamak gerekir.

Örnek üzerinden gitmek gerekirse ; farz edelim ki http://www.canertosuner.com/api/getUsers/  şeklinde tanımlı JSON formatında response dönen ve yaklaşık 10 bin kişi  tarafından kullanılan bir Api'ımız var diyelim. Api'ı production'a aldıktan aylar sonra dönen responsları değiştirdiniz ve bu Api'ı kullanan kişileri de etkileyen bir değişiklik olsun. Bu şu demek; 10 bin kişinin geliştirdiği 10 bin uygulamaların patlaması demek. Bunun çözümü tabii ki de uygulamaya yeni versiyon çıkmamak değil bir ürün aldığı update'ler ile daha iyi olur. Bunun çözümü için Api'ınıza basit bir versiyon kontrolü koymak ve eski versiyonlara desteği sürdürmek.( http://www.canertosuner.com/api/getUsers?version=2 yada http://www.canertosuner.com/api/v2/getUsers ) .Böylelikle hem eski versiyonu kullananlar yeni versiyona geçene kadar uygulamalarının çalışmalarını sağlayabilirler hemde Api'ı yeni kullanmaya başlayacak olanlar direkt olarak son versiyondan başlarlar.

Api'ınızın Consistent yani tutarlı olması da son derece önemlidir. Api'ınızın geriye döndüğü response class'larının ortak bir base class içerisinde olmasına özen gösterin. Çünkü bu class sayesinde metodun döndüğü response dışında Api kullanıcısına farklı verilerde döndürebilirsiniz. Daha önce kullanmış olduğum bazı Api'larda tutarlılık neredeyse hiç yoktu. Sürekli olarak yeni gelen versiyonlarda parametre isimleri değişiyor, metod isimleri değişebiliyor, bir önceki metodda UserID'yi int gönderirken bir sonraki metodda string olarak istiyorlardı, hata mesajları sürekli olarak farklı formatta geliyordu vs. gibi örneklerle karşılaşmak mümkün. Bu gibi durumlara dikkat etmek gerekiyor. Ortak kullanılan parametreler tek bir yerden yönetilebiliyor olmalı ve major değişiklikler kolay kolay uygulanmamalı veya uygulandığında Api'ı kullanan kişiyi etkilenmesinden kaçınılmalı.

 

Son olarak yayınlamış olduğunuz yeni versiyonlarda iyi bir changelog yayınlayıp mevcut kullanıcılarınızı bilgilendirmeniz gerekir. Böylece kullanıcılar nasıl upgrade edeceklerini öğrenmiş olurlar ve Api uygulamanızda çok büyük major değişikliklere gitmemeye özen gösterin veya bunu kullanıcıları en az seviyede etkileyecek şekilde yapın ve tutarlılığı korumaya çalışın.

 

Rule 3: Flexibility

Yazmış olduğumuz Api'ların flexible yani esnek olması oldukça öçnemli bir konudur.Peki ama esnek derken neyi kastediyorum ?

Garbage in, garbage out (GIGO) programcılar tarafından iyi bilinen bir technical-term dür. Kısaca "input olarak ne verirsen output'un ona göre değişiklik gösterir" veya ne ekersen onu biçersin". Hadi biraz daha basit söyleyelim programa yanlış bir request'te bulunursanız response'unuzda yanlış olacaktır. GIGO yu örnek olarak vermemin sebebi aslında Web Api tarafında request response ilişkisine dikkat çekmek istemem. 

Günümüzde bir çok Api doğru düzgün bir JSON desteği bile bulunmamakta ancak iyi bir Api'ın sadece JSON değil bunu yanında YAML, XML vs. gibi formatlar içinde desteği bulunmalıdır ve bunlardan hangisini return edeceği bilgisini de aslında Api kullanıcılardan yani client'lardan parametre olarak almalıdır. Örnek olarak http://www.canertosuner.com/api/getUsers?Format=JSON yada bu bilgiyi header'a parametre olarak eklemek aslında en doğru kullanımdır diyebiliriz Accept: application/json .

Bu kullanım sadece kullanıcıya dönecek olan response için değil aynı zamanda kullanıcının size göndereceği Post metodlarında body de bulunan request parametreleri içinde geçerlidir. Bir kulalnıcı JSON olarak gönderirken diğer bir kullanıcı XML olarak göndermek isteyebilir ve böyle bir esnekliğe sahip olabilmek oldukça önemli bir ayrıcalıktır.

Api'ınızın OData özelliğine sahip olması da bir o kadar önemlidir. OData ile Api'nızın döneceği respons'lara kullanıcılar tarafından filtreleme yapma şansı sunarsınız ve böylece kullanıcının istemediği bir veriyi dönmemiş olursunuz. Örneğin A kullanıcısı GetProducts metodundan sadece ProductID'lerini almak isterken B kullanıcısı ProductName ve ProductID alanlarını almak isteyebilir. OData bu gibi durumlar için son derece filexible(esnek) bir kullanım sunar.

 

Rule 4: Security

Güvenlik bir WebService & WebApi projesindeki en önemli noktalardan biri ancak öyle Api'lar var ki otantike olmak nerdeyse bazen imkansız oluyor. Tamam güvenlik önemli ama öyle katı ve karmaşık kurallar koyarak Api yazan firmalar var ki bazen birbiri ile ilişkili Api' metodları için 2-3 çeşit güvenlik algoritması kullandığımız olabiliyor ve ortada dökümanda yoksa nasıl requestte bulunacaksın, nasıl response alacaksın hepsi belirsiz. Bir Api developer olarak Api'ı kullanan kişiler için nasıl authenticate ve authorize olunur bilgilerini içeren kolay ve açıklayıcı örneklerle anlatıyor olmamız gerekir. 

Günümüz Web Api'larının çoğunda ortak olarak token-based authentication kullanılmakta. Kısaca; kullanıcı yapmış olduğu ilk requestte Api'a bazı güvenlik parametreleri geçer ve Api'da ona random generate edilmiş olan token bilgisini döner ve client bütün session'ı boyunca aynı token üzerinden request atıp response almaya devam eder. 

Token dışında OAuth 2 + SSL de Api'ınızı güvenli hale getirmek için yapılabilecek diğer seçenekler arasındadır. SSL her halükarda kullanıyor olmamız gerekir bunun yanında OAuth 2'yi de server-side'da uygulamakta oldukça basittir ve  OAuth 2'nin hemen hemen bütün yaygın kullanılan programlama dilleri için kütüphaneleri de bulunmaktadır.

Bunlarla birlikte güvenlik için dikkat edilmesi gereken birkaç konu dha bulunmaktadır;

  • Api'larda genellikle update, insert, delete gibi işlemler yapılabilmektedir. Bu işlemleri yapan metodları public hale getirip bütün kullanıcıların kullanımına sunarken dikkat edilmesi gerekmektedir. Her kullanıcının şöyle bir Api çağrısı yapamıyor olması gerekir "/user/delete/<id>" . Bununla ilgili her bir kullanıcı için Whitelisting Functionality bilgisi oluşturup bu listede belirtilen kurallara göre request validation işlemleri yapılabilir. 
  • OData kullanırken de select işlemleri yapılırken kullanıcının sahip olmasını istemediğimiz bilgiler için request'te bulunduğunda 406 Not Acceptable Response şeklinde status kodlar dönebiliriz. 
  • Cross-Site Request Forgery (CSRF) isteklerine akrşı Api'ı korumalıyız. Session veya cookie bazlı bir authentication yapımız var ise Api'ımızı CSRF ataklarına karşı koruduğumuzdan emin olmalıyız. Bununla ilgili faydalı dökümanları The Open Web Application Security Project (OWASP) da bulabilirsiniz. 
  • Resource dosyalarına olan erişimi yönetiyor olmamız gerekir. Örnek olarak bir banka kredi kartlarını tutan Api yazdınız ve Api içerisindeki bir dosyada kredi kartı görselleri bulunmakta. Herhangi bir kullanıcı gidipte  /account/card/view/152423 şeklinde bir istekte bulunup erişmemesi gereken bir resource dosyasına erişmemelidir.
  • Bütün request'leri validate ediyor olmamız gerekir. Her bir request'in kendine özgü parametreleri bulunur ve kullanıcı sürekli olarak saçma sapan request'lerde bulunduğunda örnek olarak; userId miz 4608 olsun ve user bilglsini getiren metoda 4608 den başlayıp random sayılar üreterek ( /account/getUserInfo/4609) başka kullanıcıların bilgileri için request'te bulunduklarında Api'ımızın bir şekilde bir güvenlik kontrolü olup bu kullanıcıyı tespit edip engellemelidir.

 

Rule 5: User Acceptance

Beşinci ve son kural aslında bu beşli arasındaki en önemli kural diyebiliriz. Bu kural ilk 4 kural boyunca bahsettiğim bütün şeylerin aslında bir ürün olarak hazırlanıp kullanıcıya sunulması ve kabul görmesini içermekte. Örneğin iyi bir dökümantasyon, kolay entegre edilebilir güvenlik adımları, yazdığımız Api'metodlarını tutorial'larla süsleyip max 15 dakikada kullanıcılar tarafından entegre edebilmelerini sağlama gibi durumlar diyebiliriz.

Api'ın kullanıcılar tarafından daha kolay kabul görmesini sağlayacak bazı spesific tavsiyeler saymak gerekirse;  

  • Kullanıcıların dökümantasyonda belirtilenlere göre Api'ınızı ilk denemeden sonra entegre edebildiğinden emin olun.
  • KISS - Keep it Simple Stupid anlayışına göre hareket etmeye çalışın. Fantastik authentication yöntemlerinden kaçının, SOAP, JSON, REST'i yeniden yaratmaya çalışmayın veya bütün platformlar tarafından kabul görmemiş herhangi bir yöntem kullanmayın.
  • Service arayüzü için dil spesifik library'i desteği vermeye çalışın. Otomatik olarak bu işlemi yapan güzel tool'lar da mevcut (Alpaca veya Apache Thrift gibi).
  • Gereken sign-up işlemlerini en basit şekilde yapın. Api'ınız bir open-source proje değilse ve sign-up & register gibi işlemler varsa bunları olabildiğince basite indirgeyip biran önce kullanıcıları yormadan api'ınınzın bulunduğu yere yönlendirin. Eğer sizin için extra bir maliyeti yoksa sign-up with social media account (facebook, google, twitter etc) özelliği sunabilirsiniz. 
  • "Satış sonrası destek" sağlayın. Bug'sız uygulama olmaz ve Api'ı kullanan kişiler bug veya herhangi bir sorun ile karşılaştıklarında en kısa ve kolay şekilde bunları size iletmesini sağlayın. Bu bir forum sitesi veya e-mail sistemi olabilir size kolayca raporlayabilsinler. Sorunu giderip yeni versiyon çıktıktan sonrada ilgili kişileri tekrardan bilgilendirmeyi ihmal etmeyin.

 

Özetle..

Iot (Internet of Things) ile birlikte hayatımızda kullanacağımız IP alıp internete çıkabilen bütün teknolojik ürünler WebService & Api ile haberleşiyor olacaklar ve beraberinde birçok yeni Api'lar yazılmasına ihtiyaç duyulacak. Bugünün verilerine göre düşünürsek; binlerce api & web service var ancak sıkıntılı olan şey kullanımları gerçekten kolay değil. Sebep olarak SOLID prensiplerine pek uymayan yazılım anlamında zayıf tasarımlı projeler olması, dökümantasyon tarafının yeterli düzeyde veya yeteri kadar kolay anlaşılabilir olmaması, örnek kod parçacıkları içermemesi, raporlanmasına rağmen çözülmeyen bug'lar etc.. bir sürü sıralayabiliriz.

Yukarıda belirtilen 5 altın kural bir çok kişi tarafından aslında ortak dile getirilen başlıklar ve Api projesi geliştirirken uyulması gereken kurallar olarak belirtilmekte. Eğer bizlerde bundan sonra yazacağımız Api'larda birazcıkta olsa bu kurallara uygulayarak hareket edersek her şey hem Api geliştiriciler hemde Api kullanan kişiler için daha kolay olacaktır.

Single Responsibility Principle

Daha önceki SOLID nedir yazısında bahsettiğim üzre sırasıyla SOLID prensiplerini örneklerle anlatmaya başlıyoruz. Bu yazıda SOLID'in S'si olan ilk prensip Single Responsibility 'den bahsedeceğim ve daha önce çalıştığım bir şirkette karşılaştığım Single Responsibility ile ilgili bir olaydan da bahsetmek istiyorum.

Bir gün şirkette bir bankacılık projesinde çalışırken Mobil tarafta geliştirme yapıyordum ve projedeki modüllerden birinin bitirilmesine 4-5 gün kalmıştı ancak WebService tarafı yetişemiyordu. WebService tarafı .Net ile geliştiriliyordu ve full-stack .Net'liğin vermiş olduğu sorumlulukla project manager'ımız benden 2-3 gün service tarafına destekte bulunmamı rica etti bende ok deyip tfs'ten projeyi çektim ve projeyi ayağa kaldırdıktan sonra ufaktan incelemeye başladım ve solution'da bulunan katmanlardan biri olan  "....Common" isimli projeyi açtım. Her projede olmazsa olmaz "CommonFunctions.cs" isimli class'ı açmamla kapatmam bir oldu. Class'ın tek suçu isminin başında "Common" olması ama o class'ı yazanlar sağ olsunlar aşağıya doğru scroll ederken VS resmen dondu. Class'ın içerisinde yanılmıyorsam 5 bin satıra yakın kod vardı.

Aslında böyle olmasının bence temel sebebi proje yaklaşık 3 yıllık bir projeydi ve her gelen tam anlamıyla projeye hakim olmadan geliştirmeye başlamış olsa gerek her şeyi bu class'a yazmışlar ve ortaya OCOP - One Class Oriented Programming (benim uydurmam) çıkmış. Bazı hesaplama metodları var, Resource manager metodları var, string format metodları var, obejct mapping metodları var... var oğlu var. İsminin başında "Common" olmasından kaynaklanan yetkiyle her şey bu class'ta.

Single Responsibility prensibi bu ve benzeri durumlar için var diyebiliriz. 

Tek bir soru ve cevap ile ;

  • Single Responsibility (tek sorumluluk) nedir ?
  • Her class'ın tek bir sorumluluk alanı olup  tek bir amaca hizmet etmesidir. 

şeklinde tanımlayabiliriz. Peki ne demek bu "Her class'ın tek bir sorumluluk alanı olup tek bir amaca hizmet etmesidir." cümlesi. 

Yazılmış olan her bir class kendi işinden sorumlu olup başka class'ların yapması gereken işlere karışmaması veya kendi sorumluluk alanına çıkmaması istenir. Çok küçük bir örnekle, toplama işlemlerini yapması beklenen class gidip de çıkartma işlemlerine yapmasın, çıkartma işlemlerini yapan başka bir class yazılır ve orda yapılması beklenir.

Daha güzel ve oop odaklı olan bir örnek üzerinden gidelim. Bir tane banka için proje geliştiriyor olalım (üstte bahsettiğim gibi değil tabi :) ) ve BankAccount adında bir class ve bu class içerisinde belli hesaplamalar sonucu tutulan field'lar bulunsun.

public class BankAccount
{
    public int AccountNumber { get; set; }
    public int AccountBalance { get; set; }
 
    public void SaveData()
    {
        //kayıt işlemlerini yapan metod
    }
}

Yukarıda ki class'a ilk baktığımızda sorunsuz güzel bir class gibi duruyor ancak SRP(Single Responsibility Principle) açısından baktığımızda SRP'yi ihlal ediyor. BankAccount class'ı içerisinde bulundurduğu methodlardan dolayı data management-calculations işlerinede karışmış durumda. Yani bu class içerisinde field'ları olan birbir obje görevimi görecek yoksa barındırdığı metodlar itibariyle bir nevi helper class'ımı olacak. Bu gibi sebeplerden dolayı class'ın aldığı sorumluluklar değişkenlik göstermektedir ve bu sorumlulukların bir arada karışıklıklara sebebiyet verebilir.

Peki ne yapacağız bu durum için ? Eğer BankAccount objemiz diğer bankalardaki hesaplar için de kullanılacağını varsayalım ve böyle bir durumda bir interface veya abstrack class oluşturmak en güzel çözüm olacaktır. 

public interface IBankAccount
{
    int AccountNumber { get; set; }
    int AccountBalance { get; set; }
}

 

Şimdi IBankAccount interface'inden implement olan BankAccount class'ını oluşturabiliriz.

public class CTBank : IBankAccount
{
    int _accountNumber;
    int _accountBalance;
 
    // IBankAccount Members
     public int AccountNumber
    {
        get
        {
            return _accountNumber;
        }
        set
        {
            _accountNumber = value;
        }
    }
 
    public int AccountBalance
    {
        get
        {
            return _accountBalance;
        }
        set
        {
            _accountBalance = value;
        }
    }
}

 

Kolay ve daha oop olacak şekilde class'ımızı oluşturduk. Şimdi sırada en başta BankAccount class'ında yazdğımız SaveData metodu için bir DataAccessLayer yazmak var. Bunun için ilk olarak yine bir interface yazalım ismi IDataService olsun.

public interface IDataService
{
    bool Save(IBankAccount account);
}

 

Bu noktaya kadar ne yaptık ? 

  1. BankAccount objesinde bulunan field'ları barındıran bir IBankAccount interface'i yazdık
  2. Bu interface'den implement olan BankAccount objesini tanımladık,
  3. DataAccess class'ı için IDataService adında bir interface tanımaldık,

Bu noktadan sonra geriye sadece DataService class'ını yazmak kalıyor. Bu class tahmin ettiğiniz gibi IDataService interfce'ini implemente edecek.

public class DataService : IDataService
{
    //IDataService Members
    public bool Save(IBankAccount account)
    {
        bool isSaved = false;
        try
        {
            //save data here 
            isSaved = true;
        }
        catch (Exception)
        {
            // error
            // log exception data
            isSaved = false;
        }
        return isSaved;
    }
}

 

Görüldüğü üzre projemiz ilk başta yazdığımız BankAccount class'ına kıyasla artık daha object-oriented ve daha yönetilip genişletilebilir hale gelmiş oldu. Tabi ki istenilen feature'a göre değişiklik gösterebilir ancak proje release olduktan sonra ki değişiklik isteklerine daha kolay ve bu değişiklerin daha hızlı entegre edilebilmesini sağlar hale geldi diyebiliriz.

Sharepoint Add People Picker Control

To add people picker on your upload.aspx or any other sharepoint aspx pages;

First create a div for picker 

<div id="PeoplePickerDiv"></div>

Write your rest api functon to get people

function initializePeoplePicker(peoplePickerElementId) {

    var schema = {};
    schema['PrincipalAccountType'] = 'User,DL,SecGroup,SPGroup';
    schema['SearchPrincipalSource'] = 15;
    schema['ResolvePrincipalSource'] = 15;
    schema['AllowMultipleValues'] = true;
    schema['MaximumEntitySuggestions'] = 50;
    schema['Width'] = '280px';

    this.SPClientPeoplePicker_InitStandaloneControlWrapper(peoplePickerElementId, null, schema);
}

Initialize Your People Picker

initializePeoplePicker('PeoplePickerDiv');

Get Selected Users

var peoplePickerUserIDArray = [];
function peoplePickerGetSelectedUsersArray() {
	
    // Get the people picker object from the page.
    var peoplePicker = this.SPClientPeoplePicker.SPClientPeoplePickerDict.PeoplePickerDiv_TopSpan;


    // Get information about all users.
    var users = peoplePicker.GetAllUserInfo();

    var userInfo = '';
    for (var i = 0; i < users.length; i++) {
        var user = users[i];
        peoplePickerSetUserIdArray(user.Key);
    }
}

function peoplePickerSetUserIdArray(loginName) {

    var context = new SP.ClientContext.get_current();
    var user = context.get_web().ensureUser(loginName);
    context.load(user);
    context.executeQueryAsync(
        Function.createDelegate(null, function() {
            peoplePickerUserIDArray.push(user.get_id());
        }),
        Function.createDelegate(null, function(se, ea) {
            alert('Error:' + ea.get_message());
        })
    );
}

SOLID Prensipleri

SOLID Candır !

SOLID'in geçmişine baktığımızda çokta uzağa gitmemize gerek yok. İlk olarak 2000'li yılların başında Michael Feathers tarafından ortaya atıldı ve sonrasında Robert C. Martin tarfından "first five principles of object-oriented programming and design" olarak "SOLID" adını aldı.

 

Neden SOLID'e ihtiyaç duyuldu ?

"Bir kere yazılan kod asla ilelebet aynı kalamaz..!" aslında SOLID'in ortaya çıkış sebebi bu cümlede gizlidir. Aylar-yıllar geçtikten sonra yazmış olduğumuz kodlara bir değişiklik yapmak istediğimizde bu değişikliği ne kadar efor sarf ederek ve ne kadar sürede yapacağız..? SOLID projeyi yazdıktan sonraki süreçte bu soruya en iyi şekilde cevap verebilmemizi sağlamak için var.

Şu gerçektir ki bazen 7 ay gibi bir efor harcanıp production'a alınmış bir proje için çok değil 6 ay sonra ki süreçte müşteri bir değişiklik veya extra modül istediğinde o modülün projeye eklenmesi neredeyse projeyi production'a almak için harcanan süre kadar sürebiliyor.

7 ay gibi bir sürede projeyi bitir canlıya al, canlıya alındıktan 6 ay sonra müşteri gelsin yeni bir feature istesin sonra müşteriye "benim bunun geliştirmesini yapmak için 6 aya daha ihtiyacım var.." dediğinizde müşteri heralde ufaktan şöyle olur "WTF...man ?"

SOLID prensiplerinin amacı aslında bizlerin daha iyi programlar yazmamızdan çok ileride istenecek değişimlere açık olup bu değişimlere en az eforla ayak uydurabilecek kodlar yazmamızı sağlamaktır. Nesne yönelimli programla yapıyorsak dünyada standart kabul edilen bu 5 prensibe uygun kodlar yazıyor olmamız gerekir.

 

 Peki nedir bu ilk 5 prensip ?

  • – Single-Responsibility Principle
  • – Open-Closed Principle
  • L  – Liskov Substitution Principle
  • I  – Interface Segregation Principle
  • D  – Dependency Inversion Principle

Her bir prensip için sonrasında ayrı ayrı blog'lar yazacağım ancak kısaca bir kaç kelimeyle şu şekilde özetleyebiliriz;

Single-Responsibility Principle

Bir class'ın sadece tek bir işi olmalı ve sadece o işten sorumlu olmalıdır. Örneğin Log tutmak için yaptığınız bir Logger class'ı sadece Log işlemlerinden sorumlu olmalıdır.

 

Open-Closed Principle

Uygulamada yazdığımız objeler yada entity'ler gelişime açık değişime kapalı olmalıdır. Örnek olarak çok if-else yazmaktan kaçının demek desek yanlış olmaz. Bir şekilde daha öcnesinde yazmış olduğunuz metodunuzu genişletmek istediğinizde interface yada abstract class'lardan faydalanarak daha yönetilebilir ve ihtiyaç duyulduğunda genişletilebilir kodlar yazmak gerekmektedir.

 

Liskov Substitution Principle

Liskov prensibi için kısaca yerine geçme prensibi diyebiliriz. Bir base class'tan türetilen class'ların yeri geldiğinde ihtiyaç halinde üst class'ların yerine de kullanabileceğini söylemekte.

 

Interface Segregation Principle

Client'lar ihtiyaç duymadıkları bir interface'i kullanmaya zorlanmamalı veya ihtiyaç duyduğu interface'e ait tek bir metod için bütün interface'in metodları implemente etmemelidir. Interface Segregation prensibi bu gibi durumlar için interface'lerinizi ayırın ve bir interface'e gerektiğinden fazla görev yüklemeyin der. Böylelikle client geliştirmesini yaparken ihtiyaç duymadığı hiç bir metodu implemente etmek zorunda kalmicaktır.

 

Dependency Inversion Principle

Bağımlılığı tersine çevirme prensibine göre base class'lar veya metodlar vs. alt seviyeli sınıflara veya metodlara bağımlı olmamalıdır ve alt class'larda yapılan bir değişiklik base class'ları etkilememelidir. Örnek olarak kısa bir süre önce gözlüklerimin camını değiştirmiştim ve nedendir bilinmez tam o sırada aklıma bu prensip gelmişti :)

Alt modül ; gözlük camı,

Üst modül ; gözlük

olsun. Gözlükçüye gittiniz ve camları değiştireceksiniz adam dedi ki "Camlar gözlüğe bağlı olduğundan camları değiştirmek için komple gözlüğü değiştirmeniz gerekmekte.."  . En iyi ihtimal adama "manyak mısın sen arkadaş.. " dersiniz. İşte bu prensip bu gibi durumlar için var. Alt modül üst modüle bağımlı olsun ama bağımlılık başımızın belası da olmasın tabi ki :)

 

Yazımız burada bitiyor ancak her bir prensip için ayrı ayrı blog yazıları yazıyor olacağım..just follow :)

Web Api Exception Handling and Logging

Herhangi bir yazılım projesinde Exception alınd��ğında bu exception'nın handle edilip belli başlı bazı süreçlerden geçirilip log atılmasına kadar geçen süreçler oldukça önemlidir ve enterprise bir projede development yapıyor isek ExceptionLog olmazsa olmazlardan biridir. Projeye yeni başlarken mimarisi ilk düşünülüp tasarlanması gerekir çünkü proje ilerledikçe log, exception handling veya cache gibi yapıları entegre etmek biraz daha zorlaşacaktır.

Web Api tarafında Exception handling ve log işlemleri yapmak biraz daha keyifli ve basit diyebiliriz çünkü Microsoft sağolsun bazı şeyleri bizim yerimize düşünüp kolaylaştırmış.

İlk olarak şöyle bir controller metodumuz olsun ve bu metoda requestte bulunduğumuzda ne gibi bir response ile karşılaşıyoruz onu görelim.

[HttpGet]
public IHttpActionResult CheckValue(int value)  
{
    if(value > 20)
    {
        throw new ArgumentOutOfRangeException();
    }
    return Ok(value);
}

Herhangi bir 3th party tool (DHC, Postman etc.) kullanarak ilgili metoda parametresi "10" olacak şekilde request attığımızda respose olarak göndermiş olduğumuz "10" değerini status code 200 - OK olacak şekilde döner.

Aynı requesti parametre "25" olacak şekilde attığımızda ise yukarıda fırlattığımız "ArgumentOutOfRangeException" hata açıklamasını status code 500 - Internal Server Error olacak şekilde döner.

Uygulamada controller seviyesinde bir exception aldığımızda olması gereken exception'nın handle edilip kullanıcıya doğrudan Exception class'ından fırlatılan hata mesajı değilde(ihtiyaca göre) ilgili daha anlamlandırılmış bir şekli response'a eklenip client'a dönüyor olması ve bu exception'nın log olarak bir yerde tutuluyor olması gerekir.

Yukarıda bahsettiğimiz gibi WebApi için Exception alındığında Log işleminin yapılmasını için kullanıma sunulan abstract class ve onun metodlarını inceleyeceğiz. 

1.Logging ExceptionLogger class Log metodunu kullanarak uygulamadaki herhangi bir exception'nın loglanmasını sağlayabiliriz. Bunun için aşağıda UnhandledExceptionLogger isimli class'ımızı kullanarak ilerleyeceğiz.

public class UnhandledExceptionLogger : ExceptionLogger  
{
    public override void Log(ExceptionLoggerContext context)
    {
        var log = context.Exception.ToString();
        //Loglama için ilgili ilemlerin yapıldığı yer (db log, file log vs gibi)
    }
}

Üstte tanımladığımız UnhandledExceptionLogger class'ını Web Api projesinde kullanabilmek için register etmemiz gerekmekte. Register işlemi ile birlikte WebApi nin otomatik olarak default set ettiği ExceptionLogger'ı kendi yazdığımız UnhandledExceptionLogger class'ı ile değiştiriyoruz. Bu işlem için aşağıdaki kodu Global.asax.cs içerisindeki Application_Start metoduna yazıyoruz.

config.Services.Replace(typeof(IExceptionLogger), new UnhandledExceptionLogger());

 

Bu adımdan sonra uygulamada bir unhandled exception fırlatıldığı anda logger'ımız onu yakalayıp yazmak istediğimiz bir yere (db, file etc.) yazmamızı sağlayacaktır.

 

2.Excepiton Hander Bu kısımda uygulamamızda fırlatılmış olan exception'nı yakalayıp response da değiştirmek istediğimiz yerleri değiştirip client'a dönme işlemlerini yapacağız. Bunun için ExceptionHandler class'ından faydalanacağız. Bu class ExceptionLogger ve ExceptionFilter dan sonra eğer ilgili exception handle edilmediyse çağrılır. GlobalExceptionHandler ismindeki class'ımız aşağıdaki gibi olacaktır.

 public class GlobalExceptionHandler : ExceptionHandler
    {
        /// <summary>
        /// This function is used for to change the response when an exception occurs.
        /// </summary>
        /// <param name="context"></param>
        public override void Handle(ExceptionHandlerContext context)
        {
            var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent(context.Exception.Message),
            };
            context.Result = new ExceptionActionResult(result);
        }
    }

 

Yukarıda exception fırlatıldığında yakalaıp client'a dönen response'u modify ettik. Bunun için kendi custom yazdığımız ExceptionActionResult class'ını kullanacağız. O da aşağıdaki gibi olacaktır.

    /// <summary>
    /// This class is a kind of Custom Action Result. When we get any exception and want to handle the web api method that returns HttpActionResult, we can use this class and it's functions.
    /// </summary>
    public class ExceptionActionResult : IHttpActionResult
    {
        private HttpResponseMessage _httpResponseMessage;

        public ExceptionActionResult(HttpResponseMessage httpResponseMessage)
        {
            _httpResponseMessage = httpResponseMessage;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(_httpResponseMessage);
        }
    }

 

Şimdi sırada 1.Adımda da yaptığımız gibi yazış olduğumuz Handler'ı WebApi'nin default set ettiğiyle değiştirme işlemi var bunu da Global.asax.cs içerisindeki Application_Start metodunda aşağıdaki gibi yapıyoruz.

config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());  

 

Uygulamamız hazır. Bu entegrasyondan sonra uygulamamıza 2 şey kazandırmış olduk.

  1. Exception alındığında Log atma işlemi
  2. Fırlatılan unhandled exception'ı yakalayıp kendi yazdığımız custom HttpActionResult ile değiştirip client'a giden respons'u daha anlamlı ve yönetilebilir bir hale getirmiş olduk.

Yazımızın en başında yazmış olduğumuz CheckValue metoduna veya uygulamada herhangi bir metoda request'te bulunup exception fırlatıldığında artık bütün response'lar artık şu şekilde olacaktır.

StatusCode : 500 Internal Server Error

Body : Specified argument was out of the range of valid values.

Gördüğünüz üzre projede exception alındığında log'umuzu atıp client'a dönen response'u da daha anlamlı bir hale getirmiş olduk. 

Ben örnek uygulamada response dönerken statusCode HttpStatusCode.InternalServerError yani 500 set ettim ancak siz projenizde kullanırken daha farklı statusCode'lar da ihtiyaca göre client'a dönebilirsiniz.

 

 

=> http://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/

NLog EventLog

Daha önce ki Log4Net yazımızda log tutmanın ne kadar önemli olduğu ve .Net tarafında kullanabileceğimiz free yapılardan biri olan Log4Net implementasyonunu incelemiştik. Bu yazıda ise bir diğer free framework olan NLOG dan bahsedeceğim. Nlog ile mail atabilir, file log'a, console'a, event log'a log, db ye log atabiliriz.

NLog ile ;

Fatal Üst Seviye : Sistem çökmeleri

Error Uygulama hataları ( Exceptions )

Warn Uyarılar, yinede uygulama çalışmaya devam edebilir.

Info Bilgilendirme herhangi bir amaçlı olabilir. Kullanıcı bilgileri güncellendi vs.

Debug Çalıştırılan sorgular, oturum süresinin bitmesi vs.

Trace Bir eylem başladı diğeri bitti gibi. Örn : Fonksiyon başlangıcı ve bitişi durumları( En Alt Seviye )

seviyelerinde log tutabiliriz. Biz bu yazıda NLog ile Event Log'a nasıl log atılır bunu inceliyor olucaz.

1.Adım İlk olarak işletim daha önceden yğklenmiş olan PowerShell programını yönetici olarak açıyoruz ve MyTestAppLog adında bir event log ve bu event log altında MyTestAppSource adında bir source oluşturuyoruz. Daha sonrasında oluşturduğumuz uygulama ve source isimlerini webconfig tarafta konfigurasyon yaparken ilgili alanlara set edicez.

PowerShell de aşağıdaki gibi code satırını yazıyoruz.

  New-EventLog -LogName MyTestAppLog -Source MyTestAppSource  

Sonradan işletim sisteminde bulunan search kısmına view event log yazarak event viewer'ı açıyoruz ve oluşturduğumuz MyTestAppLogve MyTestAppSource ekranda görmemiz gerekiyor.

 

 

2.Adım İkinci olarak VS da boş bir Web projesi açıp Nuget paket yöneticisine Install-Package NLog yazıp ilgili kütüphaneyi indiriyoruz ve projemizdeki WebConfig dosyası içerisine NLog configurasyonunu sağlayan satırları yazıyoruz.

Configuration tagları arasına aşağıdakini yazıyoruz

<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>  

Daha sonrada configuration tag'inin kapandığı yerin altına da şu şekilde yazıyoruz

<nlog
    autoReload="true"
    throwExceptions="true">
    <variable name="appName" value="MyTestApp" />
 
    <targets async="true">
        <target type="EventLog"
            log="MyTestAppLog"
            name="eventlog"
            source="MyTestAppSource"
            layout="${message}${newline}${exception:format=ToString}"/>
    </targets>

    <rules>
      <logger name="*" writeTo="eventlog" minlevel="Info" />
    </rules>
  </nlog>

 Üstteki configurasyonda ;

  • <target type="EventLog" diyerek EventLog configurasyonu olduğunu belirtiyoruz,
  • log="MyTestAppLog" EventLog adı,  
  • name="eventlog" diyerek isimlendiriyoruz,
  • source="MyTestAppSource" EventLog da tanımlı olan Source adı.
  • layout="$ ile log formatını belirliyoruz.
  • <rules> tag'i arasına yazdığımız kod satırıyla eventlog adında tanımlı olan log configurasyonu için minimum Info seviyesinde log tut.

 

3.Adım Şimdi C# tarafına geçip nasıl log atacağız onu yazalım

 public class NLogManager 
    {
        //WebConfigde tanımladığımız gibi info ve yukarı seviyeler için eventLog'a log atacaktır
        public void LogError(LogModel entry)
        {
           var logger = NLog.LogManager.GetCurrentClassLogger();
           logger.Log(LogLevel.Info, "Info Logged");
           logger.Log(LogLevel.Error, "Error Logged");
        }
    }

Uygulamamızı çalıştırdığımızda event log da aşağıdaki gibi log düştüğünü göreceğiz

 

 

 

Web Config Custom Section

WebConfig Asp.Net tabanlı projelerde istemci tarafında ilk çalıştırılan XML tabanlı bir sayfadır diyebiliriz. İçerisinde bir çok bilgi barındırır ve bir istemci yani kullanıcı websitenize erişmeye çallıştığında ilk webconfig çalışır ve içerisinde set edilen bilgilere göre projenizi çalıştırır & ayağa kaldırır.

Nedir bu bilgile diye soracak olursa; 

  • Debug, Release mode ayarı,
  • .Net Framework bilgisi,
  • Varsa Database conn string bilgisi,
  • Kullanılan .Net Kütüphanelerinin versiyon bilgileri,
  • Uygulama Debug modda nasıl Release modda nasıl çalışacak bunun bilgisi,
  • Site içi ve dışı yönlendirme ayarları vs.

Görüldüğü üzre uygulama ile ilgili bir çok ayar mevcut kısacası uygulamanın configurasyonu ile ilgili sayısızca özellik & ayar set edilebilir.

Bunun gibi ayarların dışında web.config içerisinde uygulama içi kullanılan çeşitli özel bilgiler & ayarlar da saklayabilir. Peki neden bu bilgileri burda saklıyoruz veya turuyoruz ?? Uygulamada Constant diye herhangi bir class oluşturup bunun içerisinde de tutabiliriz. Ama bu sefer ne olur derseniz. Orda tuttuğunuz bilgiyi değiştirmek için tekrardan projeyi açıp class içerisinde set edilen değeri değiştirip uygulamayı tekrardan ilgili yere deploy etmemiz gerekmekte. İşte aslında WeConfig bizi bu durumdan kurtarıyor. Webconfig dosyası deploy edildiği yerde projede ki tanımlı olduğu haliyle XML formatında tutuluyor yani uygulama içinde tanımlı olan class gibi dll'e çevrilip içini tekrardan açamayacağımız bir türde durum değil. Bu nedenle değişme olasılığı yüksek olan bilgileri WebConfig içerisinde tutup ihtiyaç duyulduğunda deploy edildiği dosyada ki folder e giderek çok rahat bir şekilde içini açıp ilgili ayarı değiştirebiliriz. Örnek olarak; uygulama kullanılan iletişim sayfasında ki email adresi veya uygulamaın herhangi bir yerinde kullanılan ve sonradan değiştirmeye açık olan bir text/metin. 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
     <system.web>
      <compilation defaultLanguage="c#" debug="true" />
    </system.web>
    
    <appSettings>
      <add key="ContanctEmailAddress" value="info@canertosuner.com" />
    </appSettings>
 </configuration>

Yukarıda da görüldüğü gibi uygulama içerisinde yönetmek istediğimiz değerleri <appSettings> içerisinde unique bir key vererek value alanlarına değerleri set edebiliriz. Bu değeri code tarafından okumak istediğimizde de bilindiği üzre şu şekilde yapıyoruz;

string emailAddress= ConfigurationSettings.AppSettings["ContactEmailAddress"];

Peki ya kendimiz aynı <appSettings> section'ı olduğu gibi bir custom secton yapmak istersek..

Case şu şekilde olsun. Kredi faizi hesaplayan bir uygulamamız var ve bu uygulamadaki faiz oranlarını webconfig den okuyup hesaplıyor. Aslında bu gibi bilgiler direkt olarak DB den de alınabilir ama bizim rojemizde webConfig den alalım. 

1.Adım WebConfigde section tanımı yapma

<configuration>
  <configSections>
    <section name="KrediFaizleri" type="MyProject.KrediFaizleriSection" />
  </configSections>
</configuration>
 

Yukarıda da görültüğü gibi KrediFaizleri adında bir section ımızın olacağı bilgisini verdik.

 

2.Adım Section ve içerisindeki elementleri yazma

</configSections>  tag'inin altında aşağıdaki gibi kodumuzu yazalım

  <KrediFaizleri> 
    <Krediler>
      <add tip="ihtiyac" oran="1.65"/>
      <add tip="tasit" oran="1.96"/>
      <add tip="konut" oran="1.20"/>
    </Krediler>
  </KrediFaizleri>

Burda Krediler diye bir array var ve içerisinde Kredi objesi olduğunu düşünelim. Bu obje içerisinde de tip ve oran adında 2 tane string property var. Aslında yukarıda ki xml kodunun tam karşılığı bu. Yukarıda 3 adet kredi için oran bilgisi girdik şimdi sırada bu değerleri webconfigden okumamızı sağlayacak C# kodlarını yazmaya geldi

 

3.Adım KrediFaizleri elementini tanımlama

Yukarıda Krediler arrayinin içerisinde Kredi objesi olduğunu söylemiştik.Aslında bu bir element. Biz adına KrediFaizleriElement diyelim ve classımız aşağıdaki gibi olacaktır.

 public class KrediFaizleriElement : ConfigurationElement
    {
        [ConfigurationProperty("tip", IsKey = true, IsRequired = true)]
        public string KrediTipi
        {
            get { return (string)this["tip"]; }
            set { this["tip"] = value; }
        }

        [ConfigurationProperty("oran", IsKey = true, IsRequired = true)]
        public string Oran
        {
            get { return (string)this["oran"]; }
            set { this["oran"] = value; }
        }
    }

 

4.Adım KrediFaizleri array'ini tanımlama

Bu adımda webconfig de ki array'i almamızı sağlayan ConfigurationElementCollection den türeyen bir class yazıyoruz. Bu class'ı kullanarak tanımladığımız section içerisinde ki collection'ı içerisindeki Element ler ile birlikte alıyoruz.

[ConfigurationCollection(typeof(KrediFaizleriElement))]
    public class KrediFaizleriCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new KrediFaizleriElement();
        }

         protected override object GetElementKey(ConfigurationElement element)
        {
            return ((KrediFaizleriElement)element).KrediTipi;
        }
    }

  

5.Adım KrediFaizleriSection class ını oluşturma

Fark ettiyseniz  en içten en dışa doğru ilerliyoruz. İlk olarak Element sonra Collection şimdide sırada Section var. Aşağıda yazılan KrediFaizleriSection webconfigde tanımladığımız KrediFaizleri tag'ini okumamızı sağlayacak olan class. İçerisinde Faizler array i var ve bu array'in içerisindede KrediFaizleriElement objeleri bulunacak.

public class KrediFaizleriSection: ConfigurationSection
    {
        [ConfigurationProperty("KrediFaizleri", IsDefaultCollection = true)]
        public KrediFaizleriCollection Faizler
        {
            get { return (KrediFaizleriCollection )this["KrediFaizleri"]; }
            set { this["KrediFaizleri"] = value; }
        }
    }

   

Son Adım Webconfig de tanımlı değerleri okuma ve filtreleme işlemi.

Bu adıma kadar şu sırayla lerledik;

  1. WebConfig de Section tanımlama,
  2. WebConfig de Section içerisindeki Collection ve Elementleri tanımlama,
  3. C# tarafında Element&Model class'ını oluşturma,
  4. C# tarafında Collection class'ını oluşturma,
  5. C# tarafında Section class'ını oluşturma,

Şimdi ise sırada bu class ları kullanarak WebConfig de tanımlı değerleri okuma işlemi var. Bunun için aşağıdaki class ı kullanabiliriz. 

 public static KrediFaizleriElement KrediOranınıGetir(string krediTipi)
        {
            //KrediFaizleri section'nını içerisindeki elementler ile birlikte aldık
            var element=
                ConfigurationManager.GetSection("KrediFaizleriSection") as KrediFaizleriSection;

             //Paramaetre olarak verilen kredi tipine göre linq sorgusu yaptık
             var returnValue = element.Faizler.Cast<KrediFaizleriElement>().SingleOrDefault(c => c.KrediTipi== krediTipi);
           
           //İlgili kredi WebConfigde varmı kontrol ettik
            if (returnValue == null)
                throw new InvalidOperationException(string.Format("Kredi Tipi {0} bulunamadı !", krediTipi));
            //Bulunan krediyi geri döndürdük
            return returnValue;
        }

İşlemlerimiz bu kadar. KrediOranınıGetir metodunu gerekli yerde çağrısını yaparak kullanabiliriz.