23 Temmuz 2013 Salı

Threading - Giriş



                 Threadler, Programda paralel ve birbirinden bağımsız işlemler yapmamıza olanak sağlar. Programımıza bazen masraflı(iş yükü büyük olan,uzun süren) işlemler yaptırmak zorunda kalabiliriz toplu mail gönderme , export(excel olur,pdf olur :) , uzun hesaplama gerektiren metodlar vb. İşte bu gibi durumlarda programımızın uzun süren işlemleri gerçekleştirirken beklenilmesi kullanıcının zamanına mal olur ve bu da canını sıkar .
                Yine basit bir örnekle konuya girelim,
                Visual Studiomuzda bir form application açalım ve formumuza iki adet button ekleyelim birisinin adı mailgonder diğerinin adı baskabirisyap olsun :) , MailGonder ve BaskaIsYap metodlarını ekleyelim , Her iki buttonumuzun click eventlerinde  metodlarımızı kullanalım ;

        private void mailGonder_Click(object sender, EventArgs e)
        {
            MailGonder();
        }

        private void baskaIs_Click(object sender, EventArgs e)
        {
            BaskaIsYap();
        }

        private void MailGonder()
        {
            MessageBox.Show("Mail Gönderme İşlemi Başladı...");
            Thread.Sleep(5000);
            MessageBox.Show("Mail Gönderme İşlemi Bitti...");
        }

        private void BaskaIsYap()
        {
            MessageBox.Show("Başka Bir İşlem Başladı");
            Thread.Sleep(5000);
            MessageBox.Show("Başka Bir İşlem Bitti");
        }


Mail Gonder Butonuna tıkladığınız anda bir messageboxta işlemin başladığını belirten bir yazı çıkacak ardından Thread.Sleep(5000); programımızı 5 saniyeliğine uyutuyoruz (konumuz threading olduğu için başka konulara dallanmak istemedim mail gönderme işlemimizin 5 saniyede gerçekleştiğini varsayalım.)sonrada işlemin bittiğine dair bir ifade messageboxla ekrana gelmesini sağlıyoruz. BaskaIsYap metodunda da aynı işlemler mevcut.
       Şimdi programı çalıştırıp mail gönder butonuna basıyoruz “Mail gönderme işi başladı” yazan kutucuk çıktıktan sonra hemen başka bir iş yap butonuna, ilk butona basmanızdan 5 saniye gecmeden basılamadığını göreceksiniz yani mail gönderim işlemi tamamlanana kadar programınızda başka bir işlem yapmanıza olanak yok tabi threadingle henüz tanışmadıysanız :)
       Uygulamamızda thread’i 2 farklı şekilde kullanabiliriz birincisi methodu bir delegeye atarız ve delegeyi beginInvoke() methoduyla uyandırırız, bu method bize Iasync tipinde bir nesne döndürür ve biz bu nesnenin methodlarıyla thread hakkında bilgiler alıp işlemler yapabiliriz diğer yöntem ise Thread classı kullanarak bir nesne oluşturmak ,bu nesneye ayrı threadde çalışmasını istediğimiz methodu vermek ve threadi başlatmak. Ben örneğime ikinci yöntemle devam edicem ;
      

      // private Thread thread1;
         private void mailGonder_Click(object sender, EventArgs e)
        {
            Thread thread1 = new Thread(MailGonder);
            thread1.Start();
        }

       // private Thread thread2;
         private void baskaIs_Click(object sender, EventArgs e)
        {
            Thread thread2 = new Thread(BaskaIsYap);
            thread2.Start();
        }

         private void MailGonder()
        {
            MessageBox.Show("Mail Gönderme İşi Başladı...");
            Thread.Sleep(5000);
            MessageBox.Show("Mail Gönderme İşi Bitti...");
        }

         private void BaskaIsYap()
        {
            MessageBox.Show("Başka Bir İş Başladı");
            Thread.Sleep(5000);
            MessageBox.Show("Başka Bir İş Bitti");
        }  
      
Önce thread sınıfından thread1 nesnesini oluşturdum , oluşturma esnasında çalışan constructor methodunda ayrı threadde çalıştırmak istediğim methodu verdim. Ve son olarak nesnenin start() methoduyla mail gönderme işlemlerimi programın işleyiş sürecinden ayırarak paralel bir süreçte gerçekleşmesin sağladım .


       Yazılarımı Kısa tutma taraftarıyım o sebepten bu yazı thread’e giriş niteliğinde  temel bilgilerden oluştu, ileride  threadpool sınıfından, Senkronizasyon sıkıntılarından ,Race Conditiondan, locktan, deadloctan ve hatta interlocktan :), monitorden, mutexden, semaphore dan bahsedeceğim yazılarda görüşmek üzere ...       

9 Temmuz 2013 Salı

Unit Of Work


        Repository Pattern'deki transactional sorunları ortadan kaldırmak istiyorsan ve aynı business classının içerisinde kullanman gereken bir sürü repository nesnen var bunları tek bir birim altından yürütmek istiyorsan o zaman doğru adrese geldin  . ( Bu arada unit of work illa repository ile kullanılacak diye bir kural yok birbirinden bağımsız da kullanılabilir ama ikisi bir olunca Zeki - Metin misali birbirlerini tamamlıyorlar adeta:) )

        Örnekte Repository Pattern ile Unit Of Work patterni birlikte kullanacağım . Banka projesi üzerinde çalışıyoruz  Müsteri ve Sube nesnelerimin olması yeterli.
   

       public  class Musteri

        {
            public int Id { get; set; }
            public string Ad { get; set; }
            public string Soyad { get; set; }
            public decimal HesabındakiPara { get; set; }

        }


       public  class Sube

        {
            public int Id { get; set; }
            public decimal KasadakiPara { get; set; }
        }

        Repository classlarımın kullanacağı methodları imzalayalım;

        public interface IMusteriRepository
        {
            void ParaEkle();
            void ParaEksilt();
        }

       public interface ISubeRepository

        {
            void ParaAl();
            void ParaGonder();
        }
     
        Repository classlarım;

        public  class SubeRepository : ISubeRepository
         {
            public void ParaAl(Sube sube, decimal miktar)
            {
                sube.KasadakiPara += miktar;
            }

            public void ParaGonder(Sube sube, decimal miktar)

            {
                sube.KasadakiPara -= miktar;
            }
        }



       public class MusteriRepository:IMusteriRepository
        {
            public void ParaEkle(Musteri musteri, decimal miktar)
            {
                musteri.HesabındakiPara += miktar;
            }

            public void ParaEksilt(Musteri musteri, decimal miktar)

            {
                musteri.HesabındakiPara -= miktar;
            }
        }
      
      Buraya kadar olan kısımda anlamadığınız birşey varsa repository pattern makalesine bir göz atmanızı rica ediyorum.   Şimdi geldik dananın kuyruğunun koptuğu noktaya. UnitOfWork classımızı tanımlayayım;

   public class UnitOfWork
        {
            private DatabaseContext _dbcontext = new DatabaseContext();
            public MusteriRepository Musteriler { get; set; }
            public SubeRepository SubeRepository { get; set; }
          
            public void Save()
            {
                _dbcontext.SaveChanges();
            }
        }
   
    Şimdi UnitOfWork classımızın işlevlerine bakalım ilk olarak repositoryleri property olarak tanımladık böylece veritabanı ile yapacağımız işlemleri tek bir nesnede topladık , ikinci olarak göze çarpan ayrıntı ise savechanges() metodunu yani modeldeki değişikliği git veritabanına kaydet diyen metod , save() metodu içine aldık bunu yapmamızdaki sebep transaction sağlıklı işlemesini sağlamak , küçük bir örnekle hemen anlatayım.


            var unitOfWork = var UnitOfWork();

            var musteri1 = var Musteri() {Ad = "Ali", HesabındakiPara=49};

            var musteri2 = var Musteri() {Ad = "Veli", HesabındakiPara = 50};

            unitOfWork.Musteriler.ParaEksilt(musteri1, 10);

            unitOfWork.Musteriler.ParaEkle(musteri2, 10);
            unitOfWork.Save();

      Yukarıdaki kod anlaşıldığı gibi aliden 10 para alıp, Veliye 10 para veriyor. Şimdi biz savechanges() metodumuzu repository classlarımızda kullansaydık yaptığımız her değişiklik veritabanına yansırdı mesela alinin parasını azalttığımız anda server'ımıza enerji kaynağı veren trafo üzerine meteor düşerse ve ya elektrik kesilip jeneratör'ün de mazotu biterse naparız (tamam biraz uç nokta oldu ama şu an aklıma başka felaket senaryosu gelmedi :)) Veli'ye de ekleyemeyeceğiz. Ee para nerde (araba nerde :)) uçtu. İşte unit of Work kullanarak repository'nin eksik kalan kısmını, kanayan yarasını yani  transaction sorununu ortadan kaldırıyoruz ,Ali'den para eksilmesi ile Veli'ye para eklenmesini aynı anda veritabanına kaydolmasını sağlıyoruz.
     

Benden bu kadar hadi selametle :)
     

8 Temmuz 2013 Pazartesi

Repository Pattern

      Birgün nette gene face senin youtube benim takılırken sana yıllar evvel free lance iş bulma sözü veren bir arkadaşın arıyor ve elinde çok büyük bir iş olduğunu söylüyor , bu işi yapmak için en uygun kişinin sen olduğunu söylüyor . Sen tabi bir pozlara falan giriyorsun :)  Analiz toplantılarına  katılıyorsun. müşteri şu anda mssql server ile çalışacaklarını ama belki bi gün oracle'a ya da mysql'e geçebileceklerini belirtiyor ve yapılacak yazılımın herhangi bir Database değişiminden en az etkilenmesi gerektiğini söylüyor.Sen tabi olur, yaparız , yazılımcıyız biz herşeyi yaparız havalarına giriyosun . Aklına birsürü şey geliyor şöyle yaparım böyle yaparım diye o gece uyuyana kadar düşünüyorsun ve tabi ki doğru düzgün birşey çıkmıyor :) Hafiften stresse giriyorsun, uykuya daldığın anda ıssız bir çölde buluyorsun kendini , karşıdan aksakallı Martin Fowler yanına yaklaşıyor ve sana bişeyler anlatmaya çalışıyor sende ona doğru gidip  "ne diyon sen dayı?" diyorsun . Ses netleşiyor ,"Repository Pattern" dediğini duyuyorsun. sen "o da neymiş" demeye kalmadan yapıyor reklamını  "Patterns of Enterprise Application Architecture" kitabım sayfa bilmem kaçı aç göreceksin diye :)

        Sende kan ter içinde uyanıyorsun (neden kan ter içinde uyanıyorsan artık :)) hemen tabi repository pattern yazıyorsun  google'a ve aramaya başlıyorsun (adam Kitabının ismini bile vermiş ne diye hala başka yerlerden öğrenmeye çalışıyorsun o da ilginç :)) ve buraya kadar sürükleniyorsun neyse lafı çok çok uzattık hemen işe koyulalım.
          Örnekte sadece ürün classım olacak;

   class Urun
    {
        public int Id { get; set; }
        public string Ad { get; set; }
    }

 
         Amaçtan sapmamak adına fazla property tanımlamadım. Şimdi yapmam gereken IUrunRepository isimli interface'i tanımlamak.

   interface IUrunRepository
    {
        void UrunEkle(Urun urun);
        void UrunSil(Urun urun);
        void UrunGuncelle(Urun urun);
        Urun dKategoriyeGoreUrunGetir(int id);
        List<Urun> TumUrunleriGetir();
    }
       
         Veritabanına etki edecek metodlarımı interface'de tanımladım.
         Sıra geldi kullanacağım veritabanın işlemlerini yapacağım  repository classını yazmaya, isimlendirme yaparken hangi veritabanı kullandığımını prefix olarak eklersem daha sonra hatırlamam açısından daha iyi olur.Eklediğim repository sınıfına interface'ide implemente ediyorum böylece doldurmam gereken metodlar geliyor.

 class MssqlUrunRepository : IUrunRepository

    {

        void UrunEkle(Urun urun)

        {
            //Sql ile ürünü vt'ye kaydetme komutu 
        }

        void UrunSil(Urun urun)

        {
            //Sql ile ürünü vt'den silme komutu 
        }

        void UrunGuncelle(Urun urun)

        {
            //Sql ile ürünü güncelleme komutu 
        }

        Urun dKategoriyeGoreUrunGetir(int id)

        {
            //Sql ile Id'ye göre vt'den ürün getirme komutu  
        }

        List<Urun> TumUrunleriGetir()

        {
            //Sql ile vt'den ürünleri getirme komutu
        }
    }
 
        Ve işte bu kadar . Artık veritabanı işlemlerini core metodlarında değilde repository classımda yapacağım böylece herhangi bir veritabanı değişikliğinde tek tek core metodlarımın içinde nerede veritabanı işlemleriyle uğraşmışım aramaya gerek kalmayacak, mesela mysql e geçtiysem MySqlUrunRepository classı yazıp IUrunRepository interface'ini implemente etmem, metodların içlerini mysql veritabanına uygun doldurmam ve bir de core işlemlerini yaptığım yerde MsSqlUrunRepository yerine MySqlUrunRepository kullanmam yeterli olacak.

         Artık sende projende repository pattern kullanarak veritabanı değişikliğini asgari maliyetle atlatabilirsin:). Ayrıca Generic bir base repository sınıfı oluşturup create,update,delete gibi her repository classına eklediğin metodları bu generic base class ta tanımlaman seni tekrarlayan işleri yapmaktan kurtaracaktır.
          Bunu yaptım da peki ben şimdi core kısmında kullanacağım bütün repositorylerin  tek tek instance'ını mı alacağım ayrıca  iyi güzel anlattın da transactional işlemleri sıkıntılı gibi duruyor onu nasıl hallederiz diyorsan o zaman Ak sakallı Martin Fowler'ın sana başka bir süprizi daha var;
Unit Of Work:)

 

7 Temmuz 2013 Pazar

Entity Framework - Code First Migration

                    Selamlar, Geçen yazımda Entity Framework Code First ile ilgili bir örnek yapmıştım , bu yazıda da bir migration örneği yapıcağım .Daha önceki yazımda yaptığım örnek üzerinden devam edeceğiz , hatırlamayanlar için geçen örneğimize bi göz atalım;

 [Table("Urunler")] 

 class Urun  
 {     
   public int Id { getset; }     
   public string Ad { getset; }    
   public Kategori Kategori { getset; }     
   public List<MusteriMusteriler getset; }  
 

[Table("Kategoriler")]  

class Kategori  
 
   public int Id getset; } 
   public string Ad getset; } 
   public List<Urun> Urunler getset; }  
 


[Table("Musteriler")] 

 class Musteri  
 
   public int  Id getset; } 
   public string Ad getset; } 
   public string Soyad getset; } 
   public List<Urun> Urunler getset; }  
   public string Eposta getset; }  


class EfContext DbContext  
   public DbSet<Urun>Urunler getset; }     
   public DbSet<Kategori> Kategori getset; }    
   public DbSet<Musteri> Musteriler getset; } 

   protected override void OnModelCreating(DbModelBuilder modelBuilder)    {     modelBuilder.Entity<Urun>().Property(dp => dp.Id).IsRequired();     modelBuilder.Entity<Kategori>().Property(dp => dp.Id).IsRequired();       modelBuilder.Entity<Kategori>().Property(dp => dp.Ad).HasMaxLength(25);        modelBuilder.Entity<Urun>().HasMany(p => p.Musteriler).WithMany(t => t.Urunler) .Map(mc => mc.ToTable("MusteriUrun"));  

  
   public EfContext() :    base(ConfigurationManager.ConnectionStrings["EfDataContext"].Name)    
 {  

  } 



     Musteri classına eposta propertysi ekledim ve şimdi bunu veritabanına migrate edeceğim. Migration yapabilmek için Package Manager Console'a ihtiyacım var.

   
           Komut satırına ilk olarak Enable-Migrations yazıyorum.


           Karşıma gelen Configuration classının Seed metodunda veritabanına veri ekleme, silme , güncelleme işlerini yapabilirim. Ben örnek olarak veritabanına bir kategori ekledim;

            Bu aşamadan sonra Console'a Add-Migration musteriyeEpostaEkleme yazıyorum.


            DbMigrationdan kalıtım alan musteriyeEpostaEkleme classındaki up metodu database de yapılacak olan işlemi, down metodu ise migrationu geri alma durumunda databasede yapılacak işlemin belirtildiği metodlar.



              En son da Update-Database diyerek değişiklikleri veritabanına kaydetmiş oluyorum. Bu yazıda burda bitmiş oluyo aynı zamanda :)






 
 
                  

Entity Framework - Code First

                Herkese Merhaba, Entity Framework Code First tekniğini göstermek için yazacağım küçük bir örnekle blog yazılarımın ilkine başlıyorum , devamı gelir hayırlısı bakalım :)
 
               Örnekte kategori,ürün ve müşteri sınıfları bulunacak,  Kategori ve Ürün arasında bire çok, Ürün ve Müşteri arasında ise çoka çok ilişki olacak.

              Öncelikle Entity-Framework kütüphanesini projemize dahil etmem gerekiyor, Herhangi bir 3rd parti tool eklemek için önümde iki seçenek var .


              Birinci seçenek; Projenin üzerine sağ tıklayarak Manage Nuget Packages For Solution’u seçerim, açılan pencerede arama kutucuğuna Entity Framework yazarım ve gelen sonuçlardan Entity Framework’ün üzerindeki install butonuna tıklarım;





             Diğer seçenek ise Package Manager Console ile kütüphaneyi projeme dahil etmek .Bunun içine üstteki menüden View => Other Windows => Package Manager Console’u seçerim ve altta açılan console’a Install-Package EntityFramework yazıp Enter' a basarım.





                  Entity Framework Kütüphanesini projeme dahil ettikten sonra sıra geldi classlarımı tanımlamaya ;


 [Table("Urunler")] 

 class Urun 
 { 
    public int Id { get; set; } 
    public string Ad { getset; }
    public Kategori Kategori { getset; } 
    public List<MusteriMusteriler getset; } 
 } 

 [Table("Kategoriler")] 

 class Kategori 
 { 
 public int Id getset; }
 public string Ad getset; }
 public List<Urun> Urunler getset; } 
 } 
 [Table("Musteriler")] 
 class Musteri 
 { 
 public int  Id getset; }
 public string Ad getset; }
 public string Soyad getset; }
 public List<Urun> Urunler getset; } 
 } 


         Başlarına koyduğum attributelerde veritabanında oluşacak olan tabloların isimlerini belirttim, eğer koymasaydım class isimlerine s takısı eklenip tablo ismi belirlenecekti yani kategoris,Musteris,Uruns isminde itici mi itici tablolarım olacaktı. 


         Şimdi Birde DbContext Sınıfından türemiş bir context classına ihtiyacım var(DbContext sınıfı System.Data.Entity namespace’nde yer almakta.) 


class EfContext : DbContext 

 { 

   public DbSet<Urun>Urunler getset; }  

   public DbSet<Kategori> Kategori getset; } 
   public DbSet<Musteri> Musteriler getset; } 

   protected override void OnModelCreating(DbModelBuilder modelBuilder) 

   { 
    modelBuilder.Entity<Urun>().Property(dp => dp.Id).IsRequired(); 
    modelBuilder.Entity<Kategori>().Property(dp => dp.Id).IsRequired();
    modelBuilder.Entity<Kategori>().Property(dp => dp.Ad).HasMaxLength(25);
    modelBuilder.Entity<Urun>().HasMany(p => p.Musteriler)
                                  .WithMany(t => t.Urunler)
                                  .Map(mc => mc.ToTable("MusteriUrun")); 
   } 

   public EfContext() : base(ConfigurationManager.ConnectionStrings["EfDataContext"].Name) 

   {
   

 } 


 Context nesnesinde neler yaptım kısaca bir özetleyeyim;

         
               • Tablo haline getirmek istediğim classları Generic alan Dbset tipinde tanımladım.
         
               • OnModelCreating metodunu override ettim, veritabanına yansımasını istediğim değişiklikleri burada belirtebilirim. Örneğin Kategori ve Urun Id’leri zorunlu alan haline getirdim ve kategori adını 25 karakterle sınırladım . ,Ayrıca Urun ve Müşteri arasındaki çoka çok ilişkiyi burada tanımladım ve bu çoka çok ilişkiden doğacak olan tablonun ismini de burada verdim.
             
                • Son olarak Constructor metodunun base’nde bir connectionString belirledim. Bunun anlamı AppConfig(web projesi yapıyorsanız webconfig) dosyasındaki “EfDataContext” isminde tanımlanmış connection string'i bul ve veritabanına bu bilgiler ile bağlan. AppConfig dosyasına adı “EfDataContext” olan connection stringide ekledim ;


                    Burda klasik connection string ten farklı olarak MultipleActiveResultSets=True yaptım. Bunun nedeni aynı connectionda birden çok sorguyu paralel olarak yapılmasını sağlamak.


                    İşim hemen hemen bitti, main metoduna da şunları yazdım mı tamamdır.


 static void Main() 

 { 
   EfContext context = new EfContext(); 
   context.Kategori.Add(new Kategori() {Ad = "KategoriAd"}); 
   context.SaveChanges(); 
 }

                Context nesnesini üretip bir kez veritabanı bağlantılı bir iş yaptırmam sql server da connectionstring yazarken belirttiğimiz isimde bir veritabanı oluşturulmasına yeter. Veritabanını oluşturdum rahat rahat select,create ,update ,delete işlemlerini yapabiliyorum. Peki ya bir değişiklik yapmak istediğim ne olacak? şimdi musteri classına bir de email property’si ekleyip çalıştırayım ve aşağıdaki hatayı alayım:)




           Yani Diyor ki, Sen yaptığın değişiklikleri veritabanına anlatmadın ki şimdi ben senin context nesnen ile veritabanını nasıl eşleyeyim.

       
          Context’in constructor metodunda Database nesnesinin SetInitializer metoduna parametre olarak dropCreateDatabaseIfModelChanges nesnesini vererek veritabanını ilgilendiren herhangi bir değişiklik yapıldığı takdirde veritabanının silinip yeni baştan inşa edilmesini sağlayabilirim.     

Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfContext>());


          Yok ben değişiklik yapmasamda her çalıştırdığımda veritabanı silinip yeniden yaratılsın diyorsam (bunu neden yaparım bilmiyorum artık ne çeşit fantezilerim varsa gerçi programlama dünyası belli olmaz :)) DropCreateDatabaseAlways nesnesini metoda veririm.             
       
 Database.SetInitializer(new DropCreateDatabaseAlways<EfContext>());
            
          Peki ben hem contextim değişsin hemde veritabanındaki verilerim silinmesin istiyorum yani yaptığım değişikliklerde veritabanım silinip yenisi yaratılmasın ben oraya veri giriyorum her seferinde data girmekle uğraşamam bi yer değiştiriyorum diye neden bütün veritabanı sıfırlansın diyorsan;

Çare Migration :)