C# Thread ve Task Kullanımı

15 Eki

İşletim sistemleri, verilen görevleri iş parçacıkları ile süreçler halinde yerine getirirler. İşletim sistemindeki bir süreç, temel olarak yürütülmekte olan bir programdır. Program içerisinde yürütülmesi istenen işler bir veya birden fazla thread (iş parçacığı) ile yürütülebilmektedir. Bilgisayar programlamada dilin sağladığı özellikler sayesinde iş parçacıkları asenkron olarak yönetilebilmektedir. Her program bir ana thread ile ayağa kalkıp işi bitene kadar bu thread ile yürütülmektedir. Programı asenkron yönetmek, programın alt süreçlerini, ana thread dışında bir veya birden fazla thread ile yürütmektir.

Özellikle çok çekirdekli işlemcilerden tam performans alabilmek için birden fazla thread ile asenkron işlemler gerçekleştirilebilmektedir. Buna multithreading denilmektedir.

C# Thread ve Task sınıfları

C# programlama dilinde asenkron işlemler için Thread ve Task olmak üzere iki sınıf tanımlanmıştır. (Linkler .Net 7.0 versiyonu doküman sayfasına aittir.)

Thread Sınıfı

Thread sınıfı bir iş parçacığı oluşturup denetler, çalışma önceliğini ayarlar ve durumunu bildirir. Geriye bir değer döndürmeyen, asenkron çalışan tek bir işlemi temsil eder. C# ortamında çalışan bir thread hakkında bazı temel bilgileri elde etmek mümkündür.

// Thread Id bilgisi
Thread.CurrentThread.ManagedThreadId

// Thread durum bilgisi
Thread.CurrentThread.ThreadState
 
// Background olup olmadığı bilgisi
Thread.CurrentThread.IsBackground

//ThreadPool'dan alınıp alınmadığı bilgisi
Thread.CurrentThread.IsThreadPoolThread

Thread nesnesi oluşuturma örneği;

// Run with new Thread
var thread = new Thread(()=>Write("*"));
thread.Start();

// Run with Main Thread 
Write("-");

void Write(string input)
{
    for (var i = 0; i < 10; i++)
        Console.Write(input);
}

// Output
--**----********----

Aynı Write metodu hem main thread hemde thread nesnesi tarafından çalıştırılmaktadır. Output incelendiğinde biraz “*” karakteri biraz “-” karakterinin ekrana yazıldığı görülmektedir.

C# dilinde yönetilen (managed) thread,ya arka planda (background) ya da ön planda (foreground) çalışır. Thread’in bu özelliğini öğrenmek için Thread.IsBackground property kullanılır.

Foreground Thread

Foreground thread, main thread’in işini bitirip sonlanmasından etkilenmez. Kendi işi bittiğinde sonlanır. Bu, C#’ta ön plandaki bir iş parçacığının ömrünün ana iş parçacığına bağlı olmadığı anlamına gelir. Thread’ler default olarak foreground özelliğinde oluşturulurlar.

var thread = new Thread(()=>Write("*"));
thread.Start();

Console.WriteLine("Main thread finished");

void Write(string input)
{
    for (var i = 0; i < 10; i++)
    {
        Thread.Sleep(100);
        Console.Write(input);
    }
}


// Output
Main thread finished
**********

thread.Start() metodu çalıştırıldıktan sonra main thread Console.WriteLine(“Main thread finished”); metodunu çağırır ve main program sonlanır. Ancak thread nesnesi, foreground özelliğinde olduğu için işini bitirene kadar çalışmaya devam eder.

Background Thread

Background thread’lerin çalışması main thread veya diğer tüm foreground thread’lerin çalışır olması durumda olmasına bağlıdır.

var thread1 = new Thread(()=>Write("*"));
thread1.IsBackground = true;
thread1.Start();

Console.WriteLine("Main thread finished");

void Write(string input)
{
    for (var i = 0; i < 10; i++)
    {
        Thread.Sleep(100);
        Console.Write(input);
    }
}


// Output
Main thread finished

Burada main thread Console.WriteLine(“Main thread finished”); metodunu çağırıp hemen sonlandığı için background thread de işini yapamadan yani ekrana birşey yazamadan sonlanmıştır. Eğer bir foreground thread2 tanımlanıp çalıştırılsaydı, o bitene kadar thread1 işini yapacaktı.

Task Sınıfı

Task sınıfı asenkron bir operasyonu temsil eder. Task-based asyncronious pattern kavramının tamel yapı taşıdır. İşlem sonucunda geriye bir değer döndürebilme özelliğine sahiptir.

Lambda ifadesi ile task nesnesi oluşturma;

Task t1 = new Task(
    () => Console.WriteLine("Working"));
t1.Start();
// t1'nin çalıştığını göstermek için main thread bloke edilsin.
t1.Wait();

Parametre alan Lambda ifadesi ile task nesnesi oluşturma;

Task t1 = new Task(
    param=> Console.WriteLine($"Working param: {param}"), 2);
t1.Start();
// t1'nin çalıştığını göstermek için main thread bloke edilsin.
t1.Wait();

Action metod task nesnesi oluşturma;

var action = (object? param) =>{
    Console.WriteLine($"I am working with param: {param}");
};

Task t1 = new Task(action, 2);
t1.Start();
// t1'nin çalıştığını göstermek için main thread bloke edilsin.
t1.Wait();

Task nesneleri arka plan (background) thread olarak hizmet vermektedir. Bu nedenle main thread sonlanmadan çalışmasını görebilmek için t1.Wait(); kullanılmıştır.

var action = (object? param) =>{
    Console.WriteLine(Thread.CurrentThread.IsBackground);
    Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
    Console.WriteLine($"I am working with param: {param}");
};

Task t1 = new Task(action, 2);
t1.Start();
t1.Wait();

Task nesnelerinin kullandıkları thread, ThreadPool’a aittir. Bunu Thread.CurrentThread.IsThreadPoolThread property ile anlayabiliriz.

ThreadPool Nedir? ThreadPool Neden Kullanılır?

ThreadPool tipi, uygulamada görevleri yürütmek, asenkron I/O süreçlerini işletmek, timer’ları işletmek gibi işlemler için bir thread havuzu sunar. Task objeleri, bu havuz içerisinden bir thread alarak işlemlerini yürütürler. Bu sayede thread oluşturma, oluşturulan thread için resource ayırma, görevi yürütme ve Garbage Colleciton ile kaynakların serbest bırakılması adımları tekrar tekrar işletilmez. ThreadPool’da arka planda bir dizi görevi gerçekleştirmek için yeniden kullanılabilecek bir thread havuzundan yeniden kullanılabilir thread’lerden bir tane alınarak işlemeler gerçekleştirilir ve thread havuza geri bırakılır.

ThreadPool üzerindeki aktif thread sayısını şu şekilde öğrenmek mümkündür;

Console.WriteLine(ThreadPool.ThreadCount);

ThreadPool üzerinde tanımlanabilecek maksimum worker thread sayısı ve maksimum I/O işlemleri için kullanılabilecek thread sayısını şu şekilde öğrenilebilir;

ThreadPool.GetMaxThreads(out var worker, out var ioCompletion);
Console.WriteLine("{0} / {1}", worker, ioCompletion);

Task içerisinde çalışan thread, ThreadPool içerisinden alınır.

Task Oluşturma ve Yürütme

Bir taskı oluşturmak ve yürütmek için birden fazla yöntem vardır. En yaygın kullanılanı ise Task.Run() metodudur. Bu yöntemde Task default parametreleri ile oluşturulur ve kullanılır.

 Task.Run(() =>
 {
     Console.WriteLine("Working");
     Console.WriteLine(Thread.CurrentThread.IsBackground);
     Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
 });

Bir diğer yöntem ise Task.Factory.StartNew() statik metodunun çalıştırılmasıdır. Bu yöntemde StartNew() metodunun overload edilmiş tipleri ile Task’a parametre geçilebilmektedir.

 Task.Factory.StartNew(() =>
 {
     Console.WriteLine("Working");
     Console.WriteLine(Thread.CurrentThread.IsBackground);
     Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
 });

Dotnet Core Configuration Ayarları

1 Eyl

ASP.NET Core’da (6.0 versiyonda) bir uygulamaya dışarıdan bir konfigürasyon ayarı, bir veya daha fazla configuration provider kullanılarak gerçekleştirilebilir. Provider diye adlandırılan araçlar, appsettings.json dosyaları veya environment variables gibi çeşitli yapılandırma kaynaklarını kullanarak konfigürasyon ayar verilerini okurlar.

Uygulamada Configuration ayarları, Program.cs dosyası içerisinde yapılabilir. Ayarların değerleri ise appsettings.json dosyası içerisinde bulunur. Örneğin aşağıdaki appsettings.json içerisinde Connection isimli bir section oluşturulmuştur.

{
  "Connection": {
    "Host": "appserver.com",
    "Port": "1524"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Connection ayarlarını uygulama içerisinde kullanmanın birden fazla yolu vardır. Bunlar aşağıda başlıklar halinde belirtilmiştir.

IConfiguration interface ile erişim

Bir class içerisinden Connection section ayarlarına erişmek için IConfiguration interface kullanılabilir. Bu interface, Microsoft.Extensions.Configuration içerisinde bulunur ve dependency injection ile istenen class içerisinden erişilebilir.

public class DatabaseService
{
  private readonly string _host;
  private readonly int _port;

  public DatabaseService(IConfiguration config)
  {
    _redisHost = config["Connection:Host"];
    _redisPort = Convert.ToInt32(config["Connection:Port"]);
  }
}

Ancak bu yöntem ile her Configuration ihtiyacı sırasında, class içerisinde section isimleri, değer isimleri ve casting işlemleri yapılmak zorundadır.

İşi bir adım daha ileriye taşıyabilmek adına, ilgili configuration section, bir nesneye dönüştürülebilir.

ConnectionOptions class:

public class ConnectionOptions
{
    public const string SectionName = "Connection";

    public string Host{ get; set; } = String.Empty;
    public string Port { get; set; } = String.Empty;
}

Options pattern ile erişim

Configuration için bir nesne tanımlandıktan sonra, ilgili class içerisinde kullanım şekli aşağıdaki gibi değiştirilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IConfiguration config)
  {
     options = new ConnectionOptions();
     config.GetSection(ConnectionOptions.SectionName)
           .Bind(options);
  }
}

Alternatif bir yol olarak ise aşağıdaki kullanım da tercih edilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IConfiguration config)
  {
     options = config.GetSection(ConnectionOptions.SectionName)
                  .Get<ConnectionOptions>();
  }
}

Options pattern kullanırken en yaygın yöntem, servis binding işlemlerinin kullanıcı class içerisinde değil de aşağıdaki gibi Program.cs içerisinde yapılmasıdır.

Options pattern IOptions<T> interface ile erişim

Configuration binding ayarlarını, tek bir extension method içerisine alarak Program.cs içeriği sadeleştirilebilir. Bu amaçla, aşağıda bir extension method tanımlanmıştır.

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             services.Configure<ConnectionOptions>(
                 config.GetSection(
                     ConnectionOptions.SectionName));

            return services;
        }
    }

Program.cs aşağıdaki gibi düzenlenebilir.

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddConfig(builder.Configuration);

var app = builder.Build();

Bu durumda DatabaseService class içeriği aşağıdaki şekilde değiştirilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IOptions<ConnectionOptions> options)
  {
     options = options.Value;
  }
}

Configuration section class’a dependency injection ile erişim

Bir Microsoft extension tipi olan IOptions<T> tipini kullanmadan, DatabaseService içerisine doğrudan ConnectionOptions tipi dependency injection yöntemi ile verilebilir. Bunun için öncelikle ConfigServiceCollectionExtensions sınıfı içerisinde küçük bir değişiklik yapılmalıdır. İki farklı alternatif ile singleton olarak ConnectionOptions tipine binding sağlayabiliriz.

ConfigServiceCollectionExtensions Seçenek-1

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             services.Configure<ConnectionOptions>(
                 config.GetSection(
                     ConnectionOptions.SectionName));
    
             services.AddSingleton(options => 
                 options => 
                 GetService<IOptions<ConnectionOptions>>().Value);

            return services;
        }
    }

ConfigServiceCollectionExtensions Seçenek-2

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             ConnectionOptions options = new();

             config.GetSection(ConnectionOptions.SectionName)
                     .Bind(options);
    
             services.AddSingleton(options);

            return services;
        }
    }

Bu durumda, DatabaseService içeriği aşağıdaki gibi yeniden düzenlenebilir. (Program.cs de hiç bir değişikliğe gerek yoktur, yukarıdaki ayarlar geçerlidir.)

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(ConnectionOptions options)
  {
     options = options;
  }
}

Artık servis sınıfı içerisinde Microsoft options pattern tipleri yerine, kendi sınıflarımızı kullanabilir hale getirmiş olduk.

C# Exception Yapısı ve Custom Exception

5 Şub

Yazılım geliştirici olarak yazdığımız kodlarda oluşabilecek istenmeyen durumlar ile başa çıkmak durumundayız. Aksi taktirde yazılımlarımız zamanla kullanılamaz hale gelebilir. İstisnai durumların oluşturulması veya oluşturulmaması üzerine zamanla bazı fikirler gelişmiştir.

Microsoft tarafından oluşturulan eski yönergelerde istisnai durumlar ile başaçıkabilmek için bazı yönlendirmeler bulunmaktadır. Bunlardan biri de temel Exception sınıfından türetilmiş SystemException ve ApplicationException şeklinde iki kategori oluşturarak istisnai durumları gruplamaya çalışmaktır.

ApplicationException

ApplicationException sınıfı System.Exception sınıfından türetilmiş bir istisnai durum bildirim türüdür. Bu türü, uygulamamızın iç yapısı ile alakalı istisnai durumlar oluştuğunda bildirimler oluşturmak amacıyla oluşturulmuştur. İstisnai durumlar bir hata veya bir uyarı olabilir. Uygulamamıza özel istisnai durum sınıflarına ihtiyaç duyduğumuzda ApplicationException sınıfından durumumuza özel yeni bir sınıf türetebiliriz. Özel istisna (custom exception) sınıfları oluşturma konusunda Microsoft tarafından oluşturulan kılavuzlarda tecrübe aktarımları mevcuttur.

SystemException

SystemException sınıfı da System.Exception sınıfından türetilmiştir. .Net Framework üzerinde CLR tarafından üretilen tüm istisnalar bu sınıf altında toparlanmıştır. Bu sınıf ile sisteme ait istisnai durum bildirimleri oluşturulmaktadır ve fetal error diye adlandırılan önemli hataların oluştuğu istisnai durumlarda kullanılırlar. Örneğin veritabanı bağlantı hataları, dosya hataları, bellek kullanım hataları oluştuğu durumlarda SystemException sınıfından türetilmiş olan sınıflar kullanılır.

İstisnai durumların kod ile ayırımı

Kodlama sırasında try-catch bloğunda istisnai durumun türüne göre tavrımızı belirleyebiliriz.

   try{
       var data = fileService.ReadFromFile();
   }
   catch(ApplciationException e){
       // Bu kısımdaki hata kontrolüm altında devam et...
   }
   catch(SystemException e){
       // Bu hata sistemsel bir sorundur log tut ve tekrar istisna gönder
       throw;
   }

SystemException türünde bir durum meydana geldiyse bu hatalar mutlaka log servisleri ile kayıt altına alınmalıdır ve görmezden gelinmemelidir.

Runtime esnasında oluşan hataları System, uygulama tarafında oluşan istisnaları Application olarak ayırmak mantıklı gibi görünebilir. Ancak bir istisnanın ölümcül(fetal) olarak değerlendirilmesi istisnanın içinde bulunduğu şartlara bağlıdır. Çalışma zamanında oluşan her hatanın ölümcül(fetal) olduğu söylenemeyeceği gibi framework dışındaki geliştiricilerin yazdığı uygulama istisnaların hepsinin göz ardı edilebilir olduğu söylenemez.

Örneğin FileNotFoundException durumu oluştuğunda her zaman programı kapatmanın anlamı yoktur. Bu durum kullanıcıya uygun bir mesaj ile iletilebilir. Hatta bazı dosyaların eksikliğinin öngörülebilmesi için faydalıdır bile denilebilir.

Bu nedenle bu başlangıç kılavuzu çıkmadı ve Microsoft da zaten çıkarmak için zorlamadı. Öte yandan şu anda .Net Framework’te bile ApplciaitonException sınıfından türetilmiş birkaç istisna bulabilirsiniz. Günümüzde yeni bir istisna türü oluşturmak isteyen biri bu iki kategoriyi kullanmak yerine Exception temel sınıfından yeni bir istisna sınıfı oluşturmaktadır. Yani System ve Application kategorisini kullanan pek yoktur.

Özel istisna türleri (custom exceptions) oluşturmak

Exception durumları hakkında kısa bir zaman yolculuğu yaptıktan sonra asıl konumuz olan özel istisna sınıflarının oluşturulmasına gelmiş bulunuyoruz.

Günümüzde geliştiricilerin kendine özel istisna türlerini oluşturmak için belli bir hiyerarşi oluşturduğunu görmek mümkündür. Örneğin:

Burada iş kurallarına ait tüm istisnalar BusinessRuleException altında toplanmıştır.

Peki bu durumda özel istisna sınıfları oluşturmak uygun bir yöntem midir? Ya da InvalidOperationException, ArgumentException gibi özel türleri mi kullanmak gereklidir? Bu iki sorunun cevabı istisnaları kimin kullanacağıdır. Kullanacak olan sizseniz özel istisnalar oluşturmak korkunç bir düşüncedir. Burada unutulmamalıdır ki istisnalar kodda istisnai bir durumu belirtmek için kullanılır. Yani beklenmeyen bir durumu anlatmak için kullanılır.

Özel istisnalar ile ilgili bir kod yapısı oluşturulduğunda aşağıdaki gibi bir durum meydana gelecektir.

public string CreateCustomer(string name, string email)
{
   try
   {
     /* Creating a customer */
   }
   catch (EmailIsNotUniqueException)
   {
       return "The email provided already registered: " + email;
   }
   catch (CustomerNameIsInvalidException)
   {
       return "Customer name is invalid: " + name;
   }
   catch (CustomerAddressIsInvalidException)
   {
       return "Customer address is invalid";
   }
}

Bu kod yapısı C# diline uygun olsa da, hatta kullanışlı görünse bile iyi bir kullanım şekli değildir. Çünkü burada program akışı kontrol edilirken istisnalar kullanılmıştır ve istisnaların kontrol akışını sağlamak gibi bir görevi yoktur. Yukarıda da bahsettiğimiz gibi istisnalar istenmeyen bir durumu belirtmek için kullanılır.

Yazdığınız kodu sadece siz kullanacaksanız özel istisna sınıfları türetmenize gerek yoktur. İstisnalar yazılımınızdaki beklenmedik hataları bildirir ve işlemi tamamen sonlandırmanıza olanak verir. Ayrıca geçersiz bir girdi parametresinde olağan dışı bir durum yoktur ve bu sadece basit bir doğrulama hatasıdır.

Kendi oluşturduğunuz istisnaları, yürütme yığınının en üst düzeyindeki try-catch bloğunda yakalamak uygun bir yöntem olacaktır. İstisnaların burada log kayıtları tutulabilir ve kullanıcıya bir uyarı mesajı gönderilebilir. Örneğin Web Api için kullanıcıya 500 mesajı verilebilir.

Özel istisna türleri (custom exceptions) ve harici kütüphaneler

Yazdığınız kodu sizin dışınızda başka geliştiriciler de kullanacaksa istisnai durumlar için farklı bir yol izlenebilir.

Yazdığınız kütüphanede ortaya çıkan beklenmedik istisnai durumları nasıl ele alacağınızı siz bilmesenizde kütüphenenizi kullanan geliştiriciler bunu biliyor olabilir. Örneğin bir FTP client uygulaması kuruyorsunuz ve ana makine client uygulamaya cevap vermiyorsa bir çok kez deneme yapabilirsiniz. Bu durumda client uygulama kendi içerisinde sürekli yeniden bağlanmayı denerse işin içinde çıkılamaz. Bunun yerine kütüphanenizi kullanan geliştiriciye bağlantı hatası fırlatıldığında geliştirici bu durumda ne yapacağını kestirebilir. Sonuçta bağlantı sağlanamaması durumu ile nasıl başa çıkacağını kütüphane kullanıcısı bilir. Bu gibi durumlara karşı her kullanıcının alacağı önlem farklı olabileceğinden bu istisnai durumun özel bir tür olarak diğer istisnai durumlardan ayrılması gerekir. İşte özel istisna türlerinin yürütüleceği yer burasıdır. Oluşturulan özel istisnalar, kütüphane kullanıcılarının karşılaşabileceği her olası soruna karşı doğru tepki vermelerine yardımcı olur.

Özet

  • Özel istisna oluştururken SystemException ve ApplicationException gibi ketegori ayrımları kullanmak yerine Exception temel sınıfından yeni istisna sınıfları üretmek daha uygundur.
  • Eğer yazdığınız kütüphaneyi sizden başka kullanacak olan yoksa, özel istisna türleri oluşturmayın. Mevcut istisna sınıflarını kullanabilirsiniz. Ya da size özel tek bir  istisna türü oluşturarak her yerde kullanabilirsiniz.
  • Eğer bir kütüphane veya framework geliştiriyorsanız ilgili kütüphaneye özel istisna türleri oluşturun. Bu sayede kütüphane geliştiricisi olarak önceden kestiremeyeceğiniz durumlara karşı kullanıcılara bir emniyet önlemi sunmuş olursunuz.

Kaynak: http://enterprisecraftsmanship.com/2016/12/08/custom-exception-types/

LINQPad Programcıların Oyun Alanı

25 Haz

LINQPad yazılımı, SqlServer, Oracle, OData servisleri gibi veri kaynakları üzerinde LINQ sorgularını kullanarak sorgular çalıştırabilen ve C#, VB, F# gibi programlama dillerini destekleyen bir editördür.

LINQPad ile desteklenen diller ve gerçekleştirilebilecek operasyonlar seçenekler halinde geliştiricilere sunulmuştur.

  • C# Expression
  • C# Statements
  • C# Program
  • VB Expression
  • VB Statements
  • VB Program
  • SQL
  • ESQL
  • F#Expression
  • F# Program

Adsız

Geliştiriciler olarak hızlı bir şekilde denemeler yapmak istediğimiz durumlar olabilir. Bu durumda hemen Visual Studio arayüzünde bir proje oluşturarak ya da mevcut bir proje üzerinde merak ettiğimiz kodları çalıştırmak durumunda kalırız. Ancak LINQPad ile hızlı bir şekilde istediğimiz sonucu hemen görebiliriz.

Örneğin C# LINQ ile rasgele şifre üretmek için hemen bir deneme yapmak istersek:

Adsız

Yazmak istediğimiz ifadeleri tamamladıktan sonra sonucu görmek için sadece .Dump() extension metodunu çalıştırmak yeterlidir.

Harici kütüphaneleri ve Nuget paketlerini referans olarak LINQPad’e eklemek ve çalıştırabilmek mümkündür.

 

Entity Framework ve ORM araçlarının Lazy Loading mantığı

21 Tem

Entity Framework veya NHibernate veya benzeri ORM araçlarının Lazy Loading özelliği ilişkili tablolardan veri çekerken geliştiricilere büyük kolaylık sağlar. Her kolaylığın bir maliyeti vardır ve ilişkili tablolardan veri çekme işlemi sırasında oluşan maliyetin faturası veri tabanlarına yansıtılmaktadır. Bu yazıda veritabanlarından ilişkili veri çekimi sırasında ORM araçlarının nasıl sorgular oluşturduğunu incelemeye çalışacağız.

İlişkili Tablolar
İlişkili Tablolar

Yukarıdaki şema Blog tablosuna bağlı birçok Article olabileceği gibi, Article tablosuna bağlı birçok Tag olabileceğini göstermektedir. (Veritabanı MsSQL Express’dir.)

Bu yapıya göre bir Blog altındaki Article listesini görmek istediğimizde ve her Article için kaç Tag olduğunu listelemek istediğimizde nasıl bir yol izlemeliyiz? Bunu  Entity Framework kullanarak  incelemeye çalışalım.


public class Blog
{
     public int Id { get; set; }
     public string Name { get; set; }
     public virtual ICollection Articles { get; set; }

     public override string ToString()
     {
         return string.Format("{0} ({1})",Name, Articles.Count);
     }
}

public class Article
{
     public int Id { get; set; }
     public int BlogId { get; set; }
     public string Title { get; set; }
     public string Content { get; set; }

     public virtual Blog Blog { get; set; }
     public virtual ICollection Tags { get; set; }

     public override string ToString()
     {
        return string.Format("{0} ({1} Tags)", Title, Tags.Count);
     }
}

public class Tag
{
     public int Id { get; set; }
     public int ArticleId { get; set; }
     public string Name { get; set; }
     public virtual Article Article { get; set; }
}

public class BloggingContext:DbContext
{
     public DbSet Blogs { get; set; }
     public DbSet Articles { get; set; }
     public DbSet Tags { get; set; }
}

Bu kısımda Entity Framework için veritabanı tablolarını temsil eden tipler oluşturulmuştur. App.config veya Web.config dosyaları içerisinde connectionString ayarlamalarını yaptıktan sonra BloggingContext nesnesi ile veritabanında işlemler gerçekleştirilebilmektedir. Bu işlem aşağıdaki gibi yapılmaktadır.

 static void Main(string[] args)
 {
    using (var context = new BloggingContext())
    {
       foreach (var blog in context.Blogs.ToList())
       {
          Console.WriteLine(blog);
          foreach (var article in blog.Articles)
          {
              Console.Write("\t");
              Console.Write(article);
              Console.Write("\n");
          }
       }
    }
 }

Run
Run

Öncelikle Blog’lar listeleniyor ve parantez içinde her Blog türüne bağlı Article sayısı yazdırılıyor. Article satırına da her Article türüne bağlı Tag sayısı yazdırılıyor.

Bu işlem gerçekleşene kadar acaba veritabanında neler olup bitiyor? Bunu anlamak için Express Profiler aracından faydalanabilirsiniz. Express Profiler aracı veritabanına giden tüm sorguları yakalayabilen bir özelliğe sahiptir.

Kodumuzu çalıştırdığımızda Express Profiler penceresinde bir dizi işlemler raporlanacaktır.

Express Profiler
Express Profiler

Entity Framework öncelikle veritabanının var olup olmadığını sorgular. Daha sonra ilgili tabloların olup olmadığını sorgular. Daha sonra MigrationHistory tablolarını sorgular. Derken bizi ilgilendiren kısım olan Blog listesini çeken sorguyu oluşturulur.

Adsız

Bu sorgu sonrasında Blog listesindeki ilk Blog’a bağlı Article listesi seçilir.

Adsız

Article listesi geldikten sonra ilgili Article için Tag listesi çekilir.

Adsız

 

Bu işlem döngü şeklinde devam eder gider.

Şu anda listelediğim örnek tablolarda toplamda 10 veya 15 veri vardır. Bu işlem yüz binlerce kayıt barındıran tablolarda yapılmaya çalışıldığında epeyce maliyetli olabilir. Maliyeti düşürmek adına JOIN ile oluşturulabilecek ham SQL sorguları kullanmak daha ucuz bir yöntem olacaktır.

SELECT B.Name, COUNT(A.Id)
FROM Blogs as B
LEFT JOIN Articles as A
ON A.BlogId=B.Id
GROUP BY B.Name

ORM araçlarına savaş açmak yerine uygun ve yerinde çözümler üretmek hayat kurtarıcı olabilir.

Güncelleme: 25.07.2015, SQL JOIN sorgusu eklendi.

C# MSMQ Mesaj Yönetimi, Okuma Yazma İşlemleri

21 Haz

MSMQ teknolojisi ile alakalı bir önceki yazıda MSMQ tanıtımını yapmış ve Windows işletim sistemlerine nasıl dahil edildiğini incelemiştik.

Bu yazıda ise mesajların MSMQ kuyruğuna nasıl yazıldığını ve kuyruktaki mesajların nasıl okunduğunu incelemeye çalışacağız.

Öncelikle mesaj nedir bundan bahsedelim. MSMQ teknolojisindeki mesaj kavramı aslında bir programlama dili ile oluşturulan nesnelerin serileştirilmiş halleridir. Bir nesne binary veya xml gibi formatlarda serileştirilebilir. Serileştirilmiş nesneler MSMQ gibi mesaj tabanlı iletişim ortamlarında mesaj olarak değerlendirilir.

MSMQ sisteminde mejsalar kuyruk denilen bir dizgede tutulur. Kuyruklar günlük hayattaki posta kutularına benzer. Postacı gelip zarfı posta kutusuna bırakır. Daha sonra ev sahibi gelip kutudaki zarfı açarak mesajı okuyarak ne yapacağını anlar ve gereğini yapar.

C# programlama dili ile MSMQ kuyruğundaki mesajları yönetmek mümkündür. Windows işletim sisteminde MSMQ kuyruğunu bilgisayar yönetimine girerek Private Queue seçeneğinden görüntüleyebiliriz.

MSMQ
MSMQ

.Net Framework içerisinde System.Messaging namespace altında mesaj trafiğini yönetebilecek tipler bulunmaktadır.

MessageQueue tipine ait üyeler yardımıyla sistemde bir kuyruk mevcut mu diye kontrol edilebilir, mevcut değilse yeni kuyruk oluşturulabilir. Mevcut kuyruğa bir mesaj eklenip, kuyruktan bir mesaj alınabilir. Aşağıdaki örnek kod ile kuyruğa mesaj yazma işlemi gerçekleştirilmektedir.

MessageQueue messageQueue = null;

if (MessageQueue.Exists(@".\Private$\EmailQueue"))
{
    messageQueue = new MessageQueue(@".\Private$\EmailQueue");
}
else
{
    // Kuyruk oluştur.
    MessageQueue.Create(@".\Private$\EmailQueue");
    messageQueue = new MessageQueue(@".\Private$\EmailQueue");
}

messageQueue.Send("Bu ilk bir MSMQ mesajıdır", "Title");

Kuyruktaki mesajları okumak senkron veya asenkron şekilde yapılabilmektedir. MessageQueue tipine ait aşağıdaki metodlar:

  • BeginPeek()
  • BeginReceive()

asenkron şekilde mesajları okumaya olanak sağlar. bu mesajların override edilmiş şekilleri burada mevcuttur.

BeginPeek() metodu çağrılmadan önce PeekComplated event handler oluşturmak gerekir ki okunan mesajın içeriğine erişebilelim. Aynı şekilde BeginReceive() metodu çağrılmadan önce ReceiveComplated event handler oluşturmak gerekir. BeginPeek() ile alınan mesajlar kuyruktan silinmezken BeginReceive() ile okunan mesajlar kuyruktan silinir. Bu fark, süreçleri yönetmek açısından önemlidir. Aşağıdaki örnek kod, tek bir mesajın kuyruktan asenkron olarak alınmasını sağlar. BeginReceive() kullanıldığı için alınan mesaj kuyruktan silinecektir.

MessageQueue messageQueue = null;

if (MessageQueue.Exists(@".\Private$\EmailQueue"))
{
    messageQueue = new MessageQueue(@".\Private$\EmailQueue");
    messageQueue.ReceiveCompleted += new
           				ReceiveCompletedEventHandler(EmailReceived);
    messageQueue.BeginReceive();
}

private static void EmailReceived (Object source,
            ReceiveCompletedEventArgs asyncResult)
{
    MessageQueue mq = (MessageQueue)source;
    Message m = mq.EndReceive(asyncResult.AsyncResult);
    Console.WriteLine("Message: " + (string)m.Body);
}

MessageQueue tipinde senkron bir şekilde mesaj almak için aşağıdaki metodları kullanılabilir:

  • Peek()
  • Receive()
  • GetAllMessages()

Yani Peek() veya Receive() metodu çağrıldığında kuyruktan bir mesaj gelene kadar başka işleme geçilmez. Oysa asenkron metodlarda BeginPeek() veya BeginReceive() komutlarından sonra başka işlem varsa onlar yürütülebilmekteydi.

GetAllMessages() metodu kuyruktaki tüm mesajları getirmek için kullanılır.

 MessageQueue queue = new MessageQueue(@".\Private$\EmailQueue");

 Message[] msgs = queue.GetAllMessages();

 foreach(Message msg in msgs)
 {
     Console.WriteLine(msg.Body);
 }

Mesaj kuyruğuna bir text yerine bir nesneyi göndermek mümkündür. Bu işlem için öncelikle serileştirilecek olan tipin belirlenmesi gerekmektedir. Serileştirilecek olan tip aşağıdaki şekliyle Email adında belirlenmiş ve [Serialize] attribute ile imzalanmıştır.

[Serialize]
public class Email{
     public string From {get; set;}
     public string To {get; set;}
     public string Body {get; set;}
}

Bu nesnenin bir örneğini kuyruğa eklemek için aşağıdaki gibi bir yöntem izlenir.

Email mail = new Email{
   From = "sender@mail.com",
   To = "receiver@mail.com";
   Body = "Merhaba bu bir test mesajıdır.";
};
MessageQueue messageQueue = null;

if (MessageQueue.Exists(@".\Private$\EmailQueue"))
{
    messageQueue = new MessageQueue(@".\Private$\EmailQueue");
}
else
{
    // Kuyruk oluştur.
    MessageQueue.Create(@".\Private$\EmailQueue");
    messageQueue = new MessageQueue(@".\Private$\EmailQueue");
}

Message message = new Message();
message.Formatter = new BinaryMessageFormatter();

messageQueue.Send(message);

Temel olarak MSMQ mesaj yönetimi bu şekilde yapılmaktadır. Buradaki örnekler yazma ve okuma işlemlerini gösteren tek bir uygulama gibi anlaşılabilir. Ancak dağıtık mimarilerde kuyruğuna yazım işlemini bir uygulama yaparken, kuyruğu sürekli kontrol etme işlemini işletim sistemine bağlı bir servis sağlıyor olabilir. Ya da zamanlanmış görev mantığı ile çalışan bir client uygulama da kuyruktaki mesajları okuyarak gerekli görevleri yerine getirebilir. Senaryoları tasarlamak tamamen ihtiyaca ve beceriye kalmıştır.

Bir sonraki yazıda görüşmek dileğiyle.

Microsoft Message Queuing – MSMQ Nedir?

12 Haz

Microsoft Message Queuing ya da kısa adıyla MSMQ teknolojisi, aralarında sürekli bir bağlantı gerektirmeyen dağıtık sistemlerde uygulamaların emniyetli bir şekilde iletişim kurmalarını sağlayan bir protokoldür.

Dağıtık sistemlerde çalışan bir uygulama MSMQ üzerinde kuyruğa bir mesaj gönderir ve sıradaki işlemlerden kendi iç sürecini işletmeye devam eder. Sistemde bu mesaja ihtiyacı olan diğer bir uygulama bu kuyruğa giderek ilgili mesaj varsa alır ve kendi sürecini başlatır. Aşağıdaki resimde bu durum bir grafik üzerinde ifade edilmiştir.

MSMQ
MSMQ

Bu sayede birbirinden bağımsız çalışan iki ayrı sistem, birbirlerini beklemeden görevlerini yerine getirebilmektedir.

Örneğin bir web sitesi üzerinde yapılan işlemler sonrasında kullanıcı bilgilendirme amaçlı E-Posta gönderiliyor olsun. E-Posta gönderme işlemi ayrı bir mail sunucu üzerinden yapılacaktır. Kullanıcı, bir üyelik formunu doldurup gönderdikten sonra sürecin tamamlanmasını beklemeye başlar. Bu aşamada form bilgileri veri bankasına yazılır ve kullanıcıya bir bilgilendirme mesajı gönderilir. Ancak E-Posta sunucusu geç yanıt verirse veya arızalı durumda ise kullanıcının kayıt süreci yarım kalacaktır ve eksik bir işlem yapılmış olacaktır. Bunun sebebi web uygulamasının, ayrık bir sistem olan E-Posta sunucusuna bağımlı olmasıdır. Bu gibi durumları garanti altına alabilmek için kullanıcıya gönderilecek olan E-Posta bilgileri kayıt sürecinde MSMQ sistemi üzerinde kuyruğa eklenir. Bir başka uygulama bu kuyruğu kontrol ederek E-Posta gönderme işlemlerini gerçekleştirir. Yani iki ayrık sistem birbirinden izole hale getirilerek çalışma süreçleri birbirini etkilememiş olur.

MSMQ teknolojisinin kullanım amacı, dağıtık sistemlerde asenkron ve birbirinden bağımsız süreçler oluşturarak performans artışı sağlamaktır. Dağıtık sistemde çalışan bileşenlerden bir veya birkaçının devre dışı kalması durumunda sistemin iç süreçlerinin devam edebilmesi garanti altına alınabilmektedir. Ağa sürekli bağlı olmadan yürütülen işlemlerde sonuçlar sisteme mesaj şeklinde aktarılabilir ki buna sahada satış yapmak için kullanılan uygulamalar örnek verilebilir.

Kullanım alanları:

  • Kritik öneme sahip uygulamalar. Örneğin E-Ticaret, Bankacılık gibi.
  • İş akışlarının oluşturulduğu uygulamalar.
  • Gömülü sistemler.
  • Sahada satış, sipariş uygulamaları.

MSMQ teknolojisi işletim sistemi üzerinde Windows özelliği olarak gelmektedir. İsteğe bağlı açılabilen bir özelliktir. Kişisel bilgisayar ve sunucu işletim sistemlerine göre birçok versiyona sahiptir.  Aşağıdaki listede işletim sistemi türlerine göre MSMQ versiyonlarının gelişimi sunulmuştur.

MSMQ Versiyonları:

  • MSMQ 1.0, Microsoft Windows NT, Windows 95, 98 sürümleri üzerinde çalışan versiyondu.
  • MSMQ 2.0, Microsoft Windows 2000 üzerinde çalışan versiyon.
  • MSMQ 3.0, Microsoft Windows XP Professional ve Server 2003 ailesi üzerinde çalışan versiyondur.
  • MSMQ 4.0 Windows Vista ve Windows Server 2008 üzerinde çalışır.
  • MSMQ 5.0 Windows 7 ve Windows Server 2008 R2 üzerinde çalışır.
  • MSMQ 6.0 Windows 8 ve Windows Server 2012 üzerinde çalışır.
  • MSMQ 6.3 Windows Server 2012 R2 üzerinde çalışır.

Windows 7 veya Windows 8 gibi kişisel işletim sistemlerinde MSMQ özelliğinin açılması Denetim Masası -> Programlar ve Özellikleri -> Windows Özelliklerini Aç veya Kapat linkinden açılan pencereden yapılmaktadır.

Windows 8 MSMQ
Windows 8 MSMQ

Windows 2008 veya 2012 gibi sunucu işletim sistemlerinde ise Server Manager -> Features -> Add Feature linkinden açılan pencereden seçerek yapabiliriz.

Windows 2008
Windows 2008

MSMQ özelliği işletim sistemine eklendikten sonra Bilgisayar Yönetimi menüsünden görüntülenebilmektedir.

MSMQ
MSMQ

Bir sonraki yazıda görüşmek üzere.

Kaynak: https://msdn.microsoft.com/en-us/library/ms711472(v=vs.85).aspx

C# LINQ ile Rastgele Şifre Üretmek

4 Nis

Bu yazıda System.Linq kütüphanesinden faydalanarak rastgele şifre üretmenin ne kadar pratik ve basit olduğunu inceleyeceğiz. Bu pratik çözüme burada rastladım ve herkese faydalı olacağını tahmin ederek paylaşmak istedim.

var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
    Enumerable.Repeat(chars, 8)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());

Olayın gelişimini incelediğimizde Enumerable sınıfının Repeat fonksiyonundan faydalanıldığını görüyoruz.

Enumerable.Repeat fonksiyonunun syntax düzeni şu şekildedir:

public static IEnumerable<TResult> Repeat<TResult>(
   TResult element,
   int count
)

Çalışma şekli ise TResult türünde Count kadar tekrar eden bir yığın üretecek şekilde ayarlanmıştır. Örneğin 15 tane “Okul” sözcüğü üret şeklide çalışır.

Yukarıdaki şifre üretmek için oluşturulan örnekte bizim verdiğimiz bir diziden her defasında rastgele bir karakter çekildiği için tekrarlı veri yığını oluşmuyor. Sonuç olarak count değeri kadar karakter içeren bir şifre oluşturabiliyoruz.

.Net Framework Gelişim Tarihine Genel Bakış

7 Mar

Microsoft .Net Framework ile ilgili tarihi geçmişe bir göz atarak eskiden günümüze(2015) yani 4.5.1 framework sürümüne kadar nasıl bir gelişimin olduğunu genel hatlarıyla incelemeye çalışalım.

.Net Framework 1.0: 2002 yılında duyurulmuş ilk versiyonsdur. Visual Studio .Net geliştirme aracı ile birlikte sunulmuştur.

.Net Framework 1.1: 2003 yılında duyurulan bu versiyon ile birlikte Visual Studio 2003 geliştirme aracı da piyasaya sürülmüştür. .Net Framework 1.1 ile birlikte ilk defa Asp.Net Mobile control araçları denenmiştir. Ayrıca birden fazla ve farklı Framework sürümünü aynı bilgisayarda çalıştırabilme özelliği olan side-by-side özelliği eklenmiştir. Güvenlik ile ilgili yeni gelişmeler eklenmiştir.

.Net Framework 2.0: 2005 yılında Visual Studio 2005 geliştirme aracıyla birlikte duyurulmuştur. Bu framework ile birlikte Generic, Nullable tipleri, IPV6 desteği ve CLR 2.0 gibi önemli özellikler eklenmiştir.

.Net Framework 3.0: 2006 yılında duyurulmuş olup Visula Strudio 2005 üzerinden devam etmiştir. Bu sürüm ile birlikte WCF(Windows Communication Framework), WPF(Windows Presentation Framework), WF(Workflow Foundation) gibi yenilikler geliştiricilerle buluşturulmuştur. Bu yenilikler ile iletişim, sunum  ve iş akışları gibi ayrımlar ilk defa belirgin bir şekilde altyapı olarak yapılmış oldu.

.Net Framework 3.5: 2008 yılında Visual Studio 2008 gelitirme aracı ile beraber duyurulmuştur. LINQ ve Addin/Plugin Model gibi özellikler eklenmiştir.

.Net Framework 4.0: 2010 yılında Visual Studio 2010 il birlikte duyurulan bu sürümde bir çok yeni özellik eklenmiştir. Parallel Computing, Code Contracts, Lazy Initializations, Dynamic Language RuntimeIn-process side-by-side hostingBackground garbage collection ve CLR 4.0 şeklinde önemli eklemeler yapılmıştır.

.Net Framework 4.5: 2012 yılında Visual Sturio 2012 ile beraber duyurulmuştur. Paralel Programlama üzerinde yeni gelişmeler async/await yöntemi, asenkron işlemler, 64-bit platformlarda 2 gigabayt’tan (GB) büyük diziler için destek, Sıkıştırılmış dosyaların boyutunu azaltmak için Zip sıkıştırma işlevinde geliştirmeler yapılmıştır.

.Net Framework 4.5.1: 2013 yılında Visual Studio 2013 ile birlikte duyurulmuştur. Derlemeler için otomatik bağlama yeniden yönlendirme, .NET Framework güncelleştirmelerinden sonra daha hızlı uygulama başlatma, çok çekirdekli JIT geliştirmeleri ve ASP.NET uygulama askıya alma gibi ek performans artışları yapılmıştır. Sunucularda arka plan çöp toplama işlemi ile daha iyi performans sağlanmıştır.

Prime Factors Kata C# – Kod Kata

28 Tem

Prime Factors Türkçe olarak Asal Çarpanlar şeklinde tercüme edilebilir. Prime Factors Kata’sını geliştirirken bir sayının asal çarpanlarının bulunması hedeflenmektedir. Bir sayının asal çarpanları bulunup liste şeklinde kullanıcıya verilmektedir. Aşağıdaki listede örnek olarak verilen sayıların çarpanları listelenmiştir.

Prime Factors (Asal Çarpanlar)

1 => [ ]
2 => [2]
3 => [3]
4 => [2,2]
5 => [5]
6 => [2,3]
7 => [7]
8 => [2,2,2]
9 => [3,3]
10 => [2,5]
24 => [2,2,2,3]