Caner Tosuner

Leave your code better than you found it

Parallel LINQ Nedir

Linq(Language Integrated Query), heralde .Net framework'e ait en önemli 4-5 özellikten birisidir desek kimsenin itirazı olmaz. .NET 3.5 ile hayatımıza giren linq temelde; senkron bir biçimde veri üzerinde çeşitli query'ler yazıp filtreler oluşturabildiğimiz çok zengin içeriği olan bir .Net teknolojisidir. 

Parallel Linq (PLINQ), IEnumerable yada Enumerable<T> türündeki data-source'lar üzerinde linq işlemlerini paralel bir şekilde async olarak yapıalbilmesini sağlayan yapıdır. Bu işlemler PLinq'e ait bazı extension metotlar üzerinden yapılabilmektedir ve bu metotlar sayesinde eşzamanlı olarak işlem process edilebilmektedir. 

Örnek üzerinden ilerleyecek olursak, console'a 0-100 arası 4'e bölünebilen sayıları önce linq sonrada plinq kullanarak yazdıralım. İlk olarak aşağıdaki gibi kullanacağımız veriyi hazırlayalım.

    var source = new List<int>();
    for (int i = 0; i <= 100; i++)
    {
        source.Add(i);
    }

Linq kullanarak aşağıdaki kod bloğunu yazıp projeyi run edelim.

    Console.WriteLine("Non Parallel Result\n");
    var nonParallelResults =
                     from item in source
                     where item % 4 == 0
                     select item;

    foreach (int item in nonParallelResults)
    {
        Console.Write(item + " ");
    }
    Console.WriteLine("\n");
    Console.WriteLine("\n");

Projemizi run ettiğimizde aşağıdaki gibi sırasıyla sayıları ekrana display edecektir.

Şimdi ise aynı işlemi plinq kullanarak paralel olarak yaptıralım.

    Console.WriteLine("Parallel Result\n");
    var parallelResults =
                     from item in source.AsParallel()
                     where item % 4 == 0
                     select item;
    foreach (int item in parallelResults)
    {
        Console.Write(item + " ");
    }

Tekrardan projeyi run ettiğimizde ekran çıktısı aşağıdaki gibi olacaktır.

 Yukarıdaki işlemi paralel biçimde yapmamızı sağlayan şey .AsParallel() extension metodu dur. Linq kullanarak baktığımızda 4'e bölüne bilen sayıları küçükten büyüğe sıralayarak ekrana print ettiğini görürken PLinq kullanarak sayıların herhangi bir sıraya uymadan paralel bir şekilde print edildiğini gördük.

Source üzerinde Linq mu yoksa PLinq mu kullanılacağı tamamiyle yapacağınız iş ile ilgili bir durum yani "it depends on the business". Eğer sırasıyla devam etmesini istediğiniz bir process değilse ve process edilen işlemler arası herhangi bir bağımlılık vs. bulunmuyorsa PLinq çok rahat bir şekilde kullanabiliriz. 

NULL Object Pattern Nedir ?

NULL Object Pattern Gang of Four’s Design Patterns kitabında anlatılmış olup behavioral design pattern'ler den biridir. Bu pattern'in amacı uygulama içeresinde null objeler return etmek yerine ilgili tipin yerine geçen ve expected value'nun null objesi olarak kabul edilen tipi geriye dönmektir diğer bir değişle null yerine daha tutarlı nesneler dönmektir. Bu nesne asıl return edilmesi gereken nesnenin null değeri olarak kabul edilirken onunla aynı özelliklere sahip değildir, çok daha az bilgi içermektedir. NULL Object Pattern , süreli olarak null kontrolü yaparak hem server-side hemde client-side için boilerplate code yazmaya engel olmak amacıyla ortaya çıkmış bir pattern dir.

Platform yada dil farketmeksizin geliştirme yaparken sürekli olarak nullreferenceexception aldığımız durumlar olmuştur bu durumdan kurtulmak adına obj null mı değil mi diye bir sürü if/else kontrolleri yaparız. Bu pattern'i kullanarak biraz sonraki örnekte yapacağımız gibi boilerplate code'lar yazmaktan nasıl kurtulabiliriz bunu inceleyeceğiz.

Örneğimizi 2 şekilde ele alalım. İlk olarak geriye null değer return ederek çoğunlukla nasıl geliştirme yapıyoruz o case'i ele alalım, sonrasında ise NULL Object Pattern kullanarak nasıl geliştirebiliriz onu inceleyelim.

Öncelikle Customer adında bir nesnemiz var ve repository kullanarak geriye bu nesneyi return edelim. 

    public class Customer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastNae { get; set; }
        public int NumberOfChildren { get; set; }
        public string GetFullName()
        {
            return FirstName + " " + LastName;
        }
    }

Service katmanında generic bir repository yapımız varmış gibi varsayalım ve repository üzerinden GetCustomerByFirstName adında bir metot tanımlayalım.

public class CustomerService
    {
        public Customer GetCustomerByFirstName(string firstName)
        {
            return _customerRepository.List(c => c.FirstName == firstName).FirstOrDefault();
        }
    }

Sonrasında yukarıda tanımladığımız metodu call yaparak geriye customer objesini dönelim ve bazı değerleri ekrana yazdıralım.

   var customerService = new CustomerService();
   var customer = customerService.GetCustomerByFirstName("tosuner");
   Console.WriteLine("FullName : " + customer.GetFullName() + "\nNumber Of Childreen:" + customer.NumberOfChildren);

Yukarıdaki gibi customer'ın null geldiği durumda exception thrown 'system.nullreferenceexception' hatasını çoktan aldık gibi yani memory'de değeri assing edilmemiş bir yere erişmeye çalışıyoruz. Peki çözüm olarak ne yapabilirdik, ilk akla gelen aşağıdaki gibi bir kontrol olacaktır.

    var customerService = new CustomerService();
    var customer = customerService.GetCustomerByFirstName("tosuner");
    if (customer != null)
    {
        Console.WriteLine("FullName : " + customer.GetFullName() + "\nNumber Of Childreen:" + customer.NumberOfChildren);
    }
    else
    {
        Console.WriteLine("Name : Customer Not Found !" + "\nNumber Of Childreen: 0");
    }

Yukarıdaki gibi bir çözüme gittiğimizde customer objesini get ettiğimiz bir sürü yer olduğunu düşünün ve her yerde sürekli olarak null kontrolü yapıp sonrasında console'a değerleri yazıyor oluruz. Aslında bu şu deme değil;"null kontrolü yapma arkadaş !" kesinlikle bu değil tabikide ihtiyaç duyulan yerlerde bu kontrol yapılmalı hatta birçok case'de null ise throw new CustomBusinessException() vs şeklinde exception'da throw edeceğimiz durumlar olabilir. Demek istediğim yukarıdaki gibi client'a bu kontrolü olabildiğince bırakmamak.

NULL Object Pattern uygulayarak nasıl bir çözüm getirirdik ona bakalım. İlk olarak AbstractCustomer adında base sınıfımızı oluşturalım.

    public abstract class AbstractCustomer
    {
        public abstract int Id { get; set; }
        public abstract string FirstName { get; set; }
        public abstract string LastName { get; set; }
        public abstract int NumberOfChildren { get; set; }
        public abstract string GetFullName();
    }

Sonrasında Customer objesini bu sınıftan türetelim.

    public class Customer : AbstractCustomer
    {
        public override string FirstName { get; set; }
        public override string LastName { get; set; }
        public override int NumberOfChildren { get; set; }
        public override int Id { get; set; }

        public override string GetFullName()
        {
            return FirstName + " " + LastName;
        }
    }

Şimdi ise bu pattern'in getirdiği çözüm olarak geriye null value dönmeyip asıl return edilmek istenen sınıf yerine onun null olduğunu belirten bir sınıf geriye dönelim. Bu sınıfa da NullCustomer adını verelim.

    public class NullCustomer : AbstractCustomer
    {
        public override string FirstName { get; set; }
        public override string LastName { get; set; }
        public override int NumberOfChildren { get; set; }
        public override int Id { get; set; }

        public override string GetFullName()
        {
            return "Customer Not Found !";
        }
    }

Sonrasında service katmanını aşağıdaki gibi düzenleyelim.

    public class CustomerService
    {
        public AbstractCustomer GetCustomerByFirstName(string firstName)
        {
            return _customerRepository.Where(c => c.FirstName == firstName).FirstOrDefault().GetValue();
        }
    }
    public static class CustomerExtensions
    {
        public static AbstractCustomer GetValue(this AbstractCustomer customer)
        {
            return customer == null ? new NullCustomer() : customer;
        }
    }

Yukarıdaki kod bloğunda görüldüğü üzre repository null değer dönmek yerine yeni bir NullCustomer sınıfı return edecektir.

Son adım olarak da cient tarafında yazılacak kod ise yazımızın ilk başında yazdığımız kod bloğu ile aynı.

   var customerService = new CustomerService();
   var customer = customerService.GetCustomerByFirstName("tosuner");
   Console.WriteLine("FullName : " + customer.GetFullName() + "\nNumber Of Childreen:" + customer.NumberOfChildren);

Bu pattern ile;

  • null reference kontrollerinden kurtulduk,
  • duplicate kod oranını azalttık,
  • memory de değeri olmayan bir alana erişmek yerine null value görevi gören bir nesneye eriştik,
  • dahası client tarafı için daha temiz ve kolay anlaşılır bir kod bıraktık,

Daha öncede belirtiğim gibi bu pattern'i her zaman uygulama gibi bir durum söz konusu değil, daha doğrusu sürekli null check yapmak yerine bu pattern'i uygulayalım gibi bir düşünce doğru değil. Client-side geliştirme yapan developer'a bu kontrolleri yaptırmak istemediğimizde yada "ben server-side'dan hiçbir zaman null dönmicem.." şeklinde bir garanti vermek istediğinizde kullanabileceğimiz bir pattern dir.

Dapper ORM Nedir

Object relational mappers (ORMs); ilişkisel veritabanları ile programlama dillerinin nesne modelleri arasındaki uyumsuzluğu ortadan kaldırmak, kod ile database arasındaki uyumu %100 sağlamak için yazılım dünyasında uzun süredir kullanılmaktadırlar. Daha önceki ORM yazımızda Fluent NHibernate Nedir ve Kullanımı konusuna değinmiştik. Bu yazımızda ise birçok kişi tarafından en hızlı ORM olarak kabul edilen Dapper'ı inceleyeceğiz.

Dapper, Stack Overflow ekibi tarafından open-source geliştirilen lightweight bir ORM'dir. Lightweight olması sebebiyle diğer ORM'lere kıyasla çok daha hızlıdır.

Dapper, performans ve kullanım kolaylığı sağlaması düşünülerek geliştirilmiş bir ORM dir. Transaction, stored procedure yada bulk insery kullanarak static ve dynamic object binding yapabilmemizi sağlar.

Dapper Kullanımı

Dapper kullanımı için örnek basit bir app. geliştirelim. İlk olarak Vs'da Dapper_Sample adında bir ConsoleApp oluşturalım. Sonrasında nuget üzerinden Dapper'ı projemize indirip kuralım.

 Install-Package Dapper -Version 

Kurulum işlemi tamamlandıktan sonra örneğimiz şu şekilde olsun; ContosoCorp adında bir db ve bu db de Customer adında bir tablomuz olsun ve dapper kullanarak repository class ile bu tablo üzerinde CRUD işlemleri yapalım.

Öncelikle ContosoCorp db si için table create script'ini aşağıdaki gibi yazıp execute edelim ve Customer tablomuz oluşturalım.

create table Customer
(
 Id uniqueidentifier,
 FirstName nvarchar(50) not null,
 LastName nvarchar(50) not null,
 Email nvarchar(50) not null
)

İlk olarak tablomuza karşılık gelen Customer entity'sini aşağıdaki gibi oluşturalım.

    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }

Şimdi ise CRUD işlemleri için CustomerRepository adında bir class oluşturalım ve içerisini CRUD metotlarını tanımlayalım.

    public class CustomerRepository
    {
        protected SqlConnection GetOpenConnection()
        {
            var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ContosoCorpDbConn"].ConnectionString);
            connection.Open();
            return connection;
        }
    }

Config dosyamızda kullanacağımız db için gerekli olan conn. string'inin ContosoCorpDbConn adıyla yazılı olduğunu varsayalım ve GetOpenConnection() metodu ile repository metotlarında kullanacağımız SqlConnection yaratacağız.

Dapper'da bulunan Query() metodu veritabanından retrieve data işlemini yani veri almamızı sağlayan ve model mapping'i yaparak modellerimizi dolduran metoddur. Aşağıda yazacağımız All() metodu ile Customer tablosunda bulunan bütün kayıtları select edeceğiz. 

        public IEnumerable<Customer> All()
        {
            using (var conn = GetOpenConnection())
            {
                return conn.Query<Customer>("Select * From Customer").ToList();
            }
        }

İkinci metot olarak Id parametresi alarak geriye tek bir Customer objesi return eden Get() metodunu yazalım.

        public Customer Get(Guid id)
        {
            using (var conn = GetOpenConnection())
            {
                return conn.Query<Customer>("Select * From Customer WHERE Id = @Id", new { id }).SingleOrDefault();
            }
        }

Dapper'da bulunan Execute() metodu ise db de insert, update veya delete işlemlerinde kullanılır. Aşağıda yazacağımız metot ile parametre olarak verilen customer nesnesini insert edeceğiz.

        public void Insert(Customer customer)
        {
            using (var conn = GetOpenConnection())
            {
                string sqlQuery = "INSERT Customer(FirstName,LastName,Email) Values(@FirstName,@LastName,@Email)";
                conn.Execute(sqlQuery, customer);
            }
        }

Yukarıda görüldüğü üzre ado.net'e aşinalığı olan kişiler aslında bir nevi ado.net yazdığımızı göreceklerdir çünkü dapper'ın temelinde AdoçNet vardır. Son metot olarak dapper ile yukarıda yazdığımız All() metodunu birde stored-procedure kullanarak yazalım. İlk olarak db üzerinden aşağıdaki gibi GetAllCustomersSP adında stored-procedure'ü tanımlayalım.

CREATE PROCEDURE GetAllCustomersSP
AS
BEGIN
    SELECT * FROM Customer
END

Sonrasında bu procedure'ü execute eden repository metodunu yazalım.

        public IEnumerable<Customer> AllUsingSp()
        {
            using (var conn = GetOpenConnection())
            {
                return conn.Query<Customer>("GetAllCustomersSP", commandType: CommandType.StoredProcedure).ToList();
            }
        }

 Repository metotlarını tanımladık sırada bu metotları kullanmak var. Program.cs içerisinde bu metotları call ederek yazdığımız kodları test edelim.

        static void Main(string[] args)
        {
            var custRepo = new CustomerRepository();

            var customer1 = new Customer { Id = Guid.NewGuid(), FirstName = "Caner", LastName = "Tosuner", Email = "info@canertosuner.com" };
            var customer2 = new Customer { Id = Guid.NewGuid(), FirstName = "Berker", LastName = "Sönmez", Email = "info@berkersonmez.com" };

            custRepo.Insert(customer1);
            custRepo.Insert(customer2);

            //returns 2 records
            var allCustomersUsingQuery = custRepo.All();

            //returns 2 records
            var allCustomersUsingSp = custRepo.AllUsingSp();

            Console.ReadLine();
        }

Projemizi run ettiğimizde Customer tablosuna sırasıyla 2 kayıt atacaktır ve sonrasında All() ve AllUsingSp() metotlarını kullanarak tabloya select sorgusu yaptığımızda bize yukarıda kaydettiğimiz 2 kaydı return edecektir.

Dapper aynı zamanda transactional işlemleri de destekler. UnitOfWork pattern'i BeginTransaction() ve EndTransaction() metotlarını kullanarak uygulanabilir.

 

Not : Nuget de bulunan DapperExtensions kütüphanesi kullanarak gerekli mapping işlemlerini yaptıktan sonra repository metotları içerisinde sql komutları yerine aşağıdaki gibi de kullanabiliriz. Not: Bu mapping üzerinden table/column auto generate işlemi olmamakta sadece mapping işlemini sağlamakta.

Install-Package DapperExtensions 

    public class CustomerMapper : ClassMapper<Customer>
    {
        public CustomerMapper()
        {
            base.Table("Customer");
            Map(f => f.Id).Key(KeyType.Guid);
            Map(f => f.FirstName);
            Map(f => f.LastName);
            Map(f => f.Email);
        }
    }
        public IEnumerable<Customer> All()
        {
            using (var conn = GetOpenConnection())
            {
                return conn.GetList<Customer>().ToList();
            }
        }

        public Customer Get(Guid id)
        {
            using (var conn = GetOpenConnection())
            {
                return conn.Get<Customer>(id);
            }
        }

DapperExtensions kütüphanesi ile kolayca bir Generic Repository Pattern infrastructure'ı geliştirebilirsiniz. Yazının sadeliği açısından bu ben değinmedim ancak sonraki yazılarımızda bir IoC kütüphanesi kullanarak Dapper ile basitçe bir api projesi geliştireceğiz.

Dapper son derece geliştirmesi kolay ve kullanışlı bir ORM dir. NHibernate ve EntityFramework'de olduğu gibi model mapping'inizden table/column generate etmez ancak sql sorgularını oldukça hızlı bir şekilde execute eder ve result'larını POCO class'larınıza map'ler. Bu özelliğiyle developer'lar arasında oldukça popülerdir.

Factory Method Pattern Nedir Nasıl Kullanılır

Design pattern'lar geliştirme yaparken tekrar eden problemlere denenip onaylanıp çözüm olarak sunulan kalıplardır. İyi bir tasarım deseni; yazmış olduğumuz kodları temiz, okunabilir kılıp sizden sonra gelecek olan kişilere daha kolay adapte olmayı sağlamalıdır.

Bu yazıda object oriented programming'in en çok tercih edilen design pattern'lerin den biri olan Creational pattern'ler grubundan Factory Pattern'i nedir ne değildir nasıl implemente edilir örnek proje ile inceleyeceğiz.

Factory Pattern

Gang of Four patternleri günümüz dünyasında en sıkı şekilde takip edilip en çok kullanılan ünlü tasarım desenleridir. Factory pattern'de bu 4 lü den biridir. Kısaca tanımı ; aynı abstract sınıf veya interface'den türeyen nesnelerin üretiminden sorumlu yapıdır. Bu pattern ile nesne yaratılma işini inheritance yoluyla client-side'dan ayırıp sub-classes'lara vermek amaçlanır.

Geliştirmekte olduğunuz uygulamaya yeni bir feature eklerken en az dokunuş ile client'ı bu duruma hiç sokmadan yapabilmek amaçlanır ve factory pattern de bu amaca yönelik olarak önerilen en önemli pattern'lerden birisidir.

Factory pattern 2 alt kategoriye ayrılabilir.

  1. Factory Method 
  2. Abstract Factory

Factory Method

Aynı interface'i veya abstract sınıfı implement etmiş etmiş factory nesnelerinin üretiminden sorumlu pattern dir.

Örnek bir case üzerinden ilerleyelim. Araç üretimi yapan bir fabrikamız olsun. Bu fabrika car, truck ve motorcycle üretebiliyor olsun. İlk olarak factory nesnelerimizin kullanacağı IVehicleFactory interface'ini ve car, truck, motorcycle nesnelerini oluşturalım.

    public interface IVehicle
    {
        void DisplayInfo();
    }

    public class Car : IVehicle
    {
        public void DisplayInfo()
        {
            Console.WriteLine("Car produced");
        }
    }

    public class Truck : IVehicle
    {
        public void DisplayInfo()
        {
            Console.WriteLine("Truck produced");
        }
    }

    public class Motorcycle : IVehicle
    {
        public void DisplayInfo()
        {
            Console.WriteLine("Motorcycle produced");
        }
    }

Yukarıdaki gibi nesneleri ve arayüzleri oluşturduktan sonra ismi VehicleFactory olan ve içerisinde geriye IVehicle döndüren ProduceVehicle adında bir sınıf tanımlayacağız. ProduceVehicle metodu VehicleType adında bir bir enum parametre olarak alacak. Bu enum'ı kullanarak factory metoduna üretmesini istediğimiz tip bilgisini geçeceğiz.

    public enum VehicleType
    {
        Car = 1,
        Truck = 2,
        Motorcycle = 3
    }

    public interface IVehicleFactory
    {
        IVehicle ProduceVehicle(VehicleType type);
    }

    public class VehicleFactory : IVehicleFactory
    {
        public IVehicle ProduceVehicle(VehicleType type)
        {
            IVehicle vehicle = null;
            switch (type)
            {
                case VehicleType.Car:
                    vehicle = new Car();
                    break;
                case VehicleType.Truck:
                    vehicle = new Truck();
                    break;
                case VehicleType.Motorcycle:
                    vehicle = new Motorcycle();
                    break;
            }
            return vehicle;
        }
    }


Factory metotlarımız da hazır artık üretime başlayabiliriz. Client dediğimiz kısım aslında tam da aşağıdaki kod parçaları oluyor Program.cs içerisinde aşağıdaki gibi üretmek istediğimiz türdeki aracı factory'e söyleyip üretebiliriz.

    class Program
    {
        static void Main(string[] args)
        {
            var vehicleFactory = new VehicleFactory();

            IVehicle vehicleCar = vehicleFactory.ProduceVehicle(VehicleType.Car);
            vehicleCar.DisplayInfo();

            IVehicle vehicleMotorcycle= vehicleFactory.ProduceVehicle(VehicleType.Motorcycle);
            vehicleMotorcycle.DisplayInfo();
        }
    }

Yazımızın başında da bahsettiğimiz gibi yapılabilecek değişikliklerden client'ı etkilemeden yapabilmek birinci önceliğimizdir. Bu örneğimizde araba üretimi yapmak için IVehicle interface'ini implement eden Car nesnesini kullandık ancak ilerde bir gün yine IVehicle interface'ini implement eden XCar adında bir nesne oluşturup üretim yaparken bu nesnyi kullanabiliriz ve bu durum client açısından hiç bir değişikliğe gidilmeden yapılabilmektedir.

Local Functions C# 7

Local Functions bizlere method scope'ları içerisinde function'lar tanımlamamızı sağlar ve o fonction'ı tıpkı o scope aralığında tanımlanmış bir değişken gibi kullanabiliriz. 

Local Functions C# 7.0 ile gelen oldukça özel tabir edilen feature'lar dan biridir. Local Function tanımladığımız yer bir method, property veya bir constructor olabilir ve IL kodlarına baktığımızda local function'lar compiler tarafından private metotlara dönüştürülürler.

Geliştirme yaparken bir UpdateModel adında bir metodunuzun olduğunu varsayalım ve akış gereği bu metodun içerisinde küçük operasyonel işler yapan kod parçacıkları olduğunu düşünelim. Bu kod parçacıkları sadece o metot tarafından kullanılacağından o metot dışında ayrı bir metot olarak yazma gereği duymayız. Ancak o metoda baktığımızda da belki 200 satırdan fazla koddan oluşmuş ve sürekli kendi içerisinde tekrar eden kodlar içeren bir metot haline gelmiş olabilir. Local function tam da bunun gibi durumlarda karşımıza çıkıyor. O küçük kod parçacıklarını UpdateModel metodu içerisinden ayrı küçük birer metot olarak tanımlayabiliyoruz.

Örnek olarak aşağıdaki gibi küçük bir toplama işlemi yapalım.

public class Program
{
	public static void Main()
	{ 
		int Topla(int x, int y, int z)
        {
           return x + y + z;
		}

		Console.WriteLine(Topla(3, 4, 6));
	}
}

Projeyi çalıştırdığınızda 3,4 ve 6 nın toplamı olan 13 değerini ekranda görüyor olacaksınız.

Local function'lar aynı zamanda tanımlandığı metot içerisindeki değişkenlere de access özelliği taşır. Örnek olarak aşağıdaki gibi bir local function tanımlayabiliriz.

	public static void Main()
	{ 
		int Topla(int x, int y, int z)
        {
           return x + y + z + t;
		}

		int t = 8;
		
		Console.WriteLine(Topla(3, 4, 6));
	}

Local Function'lar kısaca bu şekilde. Küçük bir feature gibi görünse de doğru yerde doğru zamanda oldukça faydalı olacakları kesindir :)

C# Compare Two Objects and Get Differences

Db de kayıtlı bulunan bir nesne için Update işlemi yaparken bazen öyle denk gelir ki sizden sadece ilgili objede değişen alanların gönderilmesi veya değişen alan var mı yok mu diye bir takım can sıkıcı validasyonlar yapmanız istenir ki hele bir de işin içine javascript girerse yemede yanında yat. İşte bu gibi durumlar için gidip sürekli olarak mevcut objedeki alanla yeni değer compare edilip değişmiş mi değişmemiş mi diye bir sürü logic yazmayı tabii ki de pek istemeyiz.

Örnek bir case üzerinden anlatmak gerekirse; aşağıdaki gibi User diye bir modeliniz ve sizden UpdateUser adından bir end-point yazmanız isteniyor ve analizde objede bulunan hangi alan kullanıcı tarafından değiştirilmiş bunlarla ilgili log tutmanız veya bilgilendirme emaili atmanız gerektiği yazılmış.

    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EMail { get; set; }
        public DateTime BirthDate { get; set; }
        public int ZipCode { get; set; }
        public bool IsPublic { get; set; }
    }

Gidip db de kayıtlı bulunan objedeki property ile kullanıcıdan aldığınız değeri teker teker compare etmek istemeyiz elbette  if(dbUserModel.FirstName == newUserModel.FirstName) . Bu gibi bir durum için Reflection'dan faydalanabiliriz. Aşağıdaki gibi bir MemberComparisonResult adında bir model tanımlayalım ve generic tanımlayacağımız CompareObjectsAndGetDifferences  adından ki metottan bu modeli döndürelim.

    public class MemberComparisonResult
    {
        public string Name { get; }
        public object FirstValue { get; }
        public object SecondValue { get; }

        public MemberComparisonResult(string name, object firstValue, object secondValue)
        {
            Name = name;
            FirstValue = firstValue;
            SecondValue = secondValue;
        }

        public override string ToString()
        {
            return Name + " : " + FirstValue.ToString() + (FirstValue.Equals(SecondValue) ? " == " : " != ") + SecondValue.ToString();
        }
    }

Şimdi ise CompareObjectsAndGetDifferences adındaki metodumuzu aşağıdaki yazalım.

        public static List<MemberComparisonResult> CompareObjectsAndGetDifferences<T>(T firstObj, T secondObj)
        {
            var differenceInfoList = new List<MemberComparisonResult>();

            foreach (var member in typeof(T).GetMembers())
            {
                if (member.MemberType == MemberTypes.Property)
                {
                    var property = (PropertyInfo)member;
                    if (property.CanRead && property.GetGetMethod().GetParameters().Length == 0)
                    {
                        var xValue = property.GetValue(firstObj, null);
                        var yValue = property.GetValue(secondObj, null);
                        if (!object.Equals(xValue, yValue))
                            differenceInfoList.Add(new MemberComparisonResult(property.Name, xValue, yValue));
                    }
                    else
                        continue;
                }
            }
            return differenceInfoList;
        }
    }

Son olarak da yazdığımız metodu aşağıdaki gibi bir ConsoleApplication'da test edelim.

        static void Main(string[] args)
        {
            var existingUser = new User
            {
                FirstName = "Mestan",
                LastName = "Tosuner",
                BirthDate = DateTime.Today.AddYears(23),
                EMail = "mestan@mestanali.com",
                ZipCode = 34000,
                IsPublic = false
            };

            var existingUserWithNewValue = new User
            {
                FirstName = "Mestan Ali",
                LastName = "Tosuner",
                BirthDate = DateTime.Today.AddYears(23),
                EMail = "mestan@mestanali.com",
                ZipCode = 55400,
                IsPublic = true
            };

            var changes = CompareObjectsAndGetDifferences(existingUser, existingUserWithNewValue);
            foreach (var item in changes)
            {
                Console.WriteLine(item.ToString());
            }
        }

Projenizi çalıştırdıktan sonra ekran çıktısı aşağıdaki gibi olacaktır.

Gördüğünüz gibi existingUserWithNewValue ismindeki modeldeki değerler kullanıcıdan yeni alınan değerlere sahip. Yukarıdaki ekran görüntüsünde de bize FirstName, ZipCode ve IsPublic alanlarının güncellendiğini söylemekte ve ilk değerleri ile ikinci değerlerini ekrana display etmekte.

Tuples Nedir - C# 7.0

Tuple custom bir class oluşturmadan üzerinde birden fazla specific değer tutabilen bir veri yapısıdır. Diğer bir değişle; geriye birden fazla parametre döndürmek istediğimiz bir metot var diyelim ve bu parametreler için normalde gidip custom bir nesne oluşturup sonra gidip bu nesneyi initialize edip property'lerini set'leyip metottan return ederdik veya bu bahsettiğimizi yeni bir object tanımlamadan out parametresini kullanarak da geriye döndürebilirdik ancak bir çok kişiye göre hem biraz maliyetli olarak kabul edilir hemde out async metotlarda kullanılamamaktadır. Bunun yerine daha basit ve anlaşılır olan tuple kullanarak çok rahat bir şekilde metottan geriye birden fazla parametre return edebiliyoruz.

Tuple ilk olarak .net 4.0 ile hayatımıza girdi. C#7.O ile birlikte kullanım olarak çok daha basit bir hale getirilmiş ve sanki ilerleyen günlerde projelerimizde biraz daka sık kullanacağız gibi duruyor. 

Tuple System.ValueTuple namespace'i altında yer almakta ve ilgili namespace'i nuget üzerinden bulup projemize indirelim.

Örnek olarak aşağıdaki gibi 3 tane integer değer alan ToplamlariCarpimlari adında Tuple özelliği olan bir metot tanımlayalım ve bu metot geriye ilk değer olarak toplamlarını ikinci değer olarak bu sayıların çarpımlarından elde dilen sayıyı return ediyor olsun.

	public static void Main()
	{
		var sonuc = ToplamlariCarpimlari(2,5,7);
		
		Console.WriteLine(sonuc.toplami);
		Console.WriteLine(sonuc.carpimi);
	}
	
	public static (int toplami, int carpimi) ToplamlariCarpimlari (int x,int y,int z)
	{
	   var t = x + y + z;
	   var c = x * y *z;
		
	   return (t,c);
	}

Yukarıda olduğu gibi main function içerisinde metodumuzu çağırdıktan sonra return ettiği değeri sanki içerisinde toplami, carpimi adında iki property barındıran bir custom nesneymiş gibi rahatça kullanabiliyoruz ve intellisense de direkt olarak nokta "." operatörü kullanarak bu return değerlerini algılayabilmekte.

Tuple kısaca bu şekilde. Basit bir özellik gibi görünsede bizleri fazladan nesneler oluşturmaktan ve async metotlar out parametresini kullanamıyorken çok rahat bir şekilde yukarıdaki metodu async olarak yazmamıza olanak sağlamakta.

Redis Nedir

Daha önceki yazıda Windows üzerinde Redis kurulumunu anlatmıştık ve o yazımızda bahsettiğimiz gibi bu yazımızda StackExchange.Redis kullanarak bir .Net Client projesi geliştireceğiz. Öncelikle pc nizde Redis'in çalıştığından emin olun. Bunun için aşağıdaki gibi bir deneme yapabilirsiniz. 

Redis çalışıp çalışmadığına dair kontrol için redis-cli.exe'yi çalıştıralım ve aşağıdaki resimde olduğu gibi bir key-value tanımlayalım ve sonrasında get set işlemi yapalım

Eğer Redis Server sorunsuz bir şekilde çalışıyorsa projemizi oluşturmaya başlayalım. Öncelikle VS'da bir adet RedisDotNetClientSample isminde bir ConsoleApplication oluşturalım.

StackExchange.Redis Kurulum

Daha sonra projemize Nuget Package Manager Console üzerinden StackExchange.Redis package'ı indirip kuralım.

PM > Install-Package StackExchange.Redis

Connection

StackExchange.Redis ile uğraşıyosanız herhalde en çok dikkat etmeniz gereken class ConnectionMultiplexer. ConnectionMultiplexer Redis Server'a bağlanmanızı sağlayacak olan sınıftır ve instance yönetimi Singleton olarak yapılması önerilir. Çünkü ConnectionMultiplexer fully thread-safe dir ve her bir işlem için tekrar tekrar instance oluşturmamız gerekir. Kurulum işlemi sorunsuz tamamlandıktan sonra aşağıdaki gibi RedisConnectionFactory adında bir class oluşturalım.

    public class RedisConnectionFactory
    {
        static RedisConnectionFactory()
        {
            lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
            {
                return ConnectionMultiplexer.Connect("localhost:6379");//redis server conn string bilgisi, web config'den almak daha doğru ancak şimdilik buraya yazdık
            });
        }

        private static Lazy<ConnectionMultiplexer> lazyConnection;

        public static ConnectionMultiplexer Connection => lazyConnection.Value;

        public static void DisposeConnection()
        {
            if (lazyConnection.Value.IsConnected)
                lazyConnection.Value.Dispose();
        }
    }

Generic ICache Interface

Connection kısmını hallettik şimdi ise Cache tarafını yazalım. Bir tane aşağıdaki gibi ICache interface'i tanımlayalım.

    public interface ICache : IDisposable
    {
        T Get<T>(string key);

        void Set<T>(string key, T obj, DateTime expireDate);

        void Delete(string key);

        bool Exists(string key);
    }

RedisCache Class'ı

Şimdi ise ICache interface'ini implement etmiş RedisCache class'ımızı oluşturalım. Bu class içerisinde tanımlı fonksiyonları kullanarak string objelerimizi json formatında string olarak Redis'e atıyor olacağız.

    public class RedisCache : ICache
    {
        private readonly IDatabase _redisDb;

        //Connection bilgisi initialize anında alınıyor
        public RedisCache()
        {
            _redisDb = RedisConnectionFactory.Connection.GetDatabase();
        }

        //Redis'e json formatında set işlemi yapılan metot
        public void Set<T>(string key, T objectToCache, DateTime expireDate)
        {
            var expireTimeSpan = expireDate.Subtract(DateTime.Now);

            _redisDb.StringSet(key, SerializerHelper.Serialize(objectToCache), expireTimeSpan);
        }

        //Redis te var olan key'e karşılık gelen value'yu alıp deserialize ettikten sonra return eden metot
        public T Get<T>(string key)
        {
            var redisObject = _redisDb.StringGet(key);

            return redisObject.HasValue ? SerializerHelper.Deserialize<T>(redisObject) : Activator.CreateInstance<T>();
        }

        //Redis te var olan key-value değerlerini silen metot
        public void Delete(string key)
        {
            _redisDb.KeyDelete(key);
        }

        //Gönderilen key parametresine göre redis'te bu key var mı yok mu bilgisini return eden metot
        public bool Exists(string key)
        {
            return _redisDb.KeyExists(key);
        }

        //Redis bağlantısını Dispose eden metot
        public void Dispose()
        {
            RedisConnectionFactory.Connection.Dispose();
        }
    }

Projemizin Redis bağlantı adımları ve set ve get işlemlerini yapan kısımları hazır. Şimdi ise yazdığımız kodları test etme adımı var. 

Örnek bir obje tanımlayıp bu objeyi redis'e atıyor olalım. Bunun için aşağıdaki gibi User adında bir class'ımız olsun.

    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }

Program.cs içerisinde aşağıdaki gibi Redis içerisine bir adet User objesi oluşturup get&set işlemi yapalım.

    class Program
    {
        static void Main(string[] args)
        {
            ICache redisCache = new RedisCache();

            var userToBeCached = new User
            {
                Id = 1,
                Email = "canertosuner@gmail.com",
                FirstName = "Caner",
                LastName = "Tosuner"
            };
            var key1 = "caner_key1";

            redisCache.Set(key1, userToBeCached, DateTime.Now.AddMinutes(30));//30 dakikalığına objemizi redis'e atıyoruz

            if (redisCache.Exists(key1))
            {
                var userRedisResponse = redisCache.Get<User>(key1);
            }
        }
    }

Projenizi çalıştırdıktan sonra aşağıdaki görselde olduğu gibi Redis'e atmış olduğumuz objeyi tekrar get işlemini yaptığımızda userRedisResponse değişkenine atanan değerleri görüyoruz.

 

Bu yazı C# ile Redis Cache ve StackExchange.Redis nasıl entegre edilip kullanılır basitçe özetlemekte. Tabiki Redis ile ilgili konuşulacak çok daha fazla konu var ancak elimden geldiğince basit bir şekilde redis client uygulaması geliştirdik.Bazı önemli linkleri aşağıda paylaşıyorum. Redis ile ilgili önemli gelişmeleri bu siteler üzerinden takip edebilirsiniz. 

redis.io 

MS OpenTech – Redis 

StackExchange.Redis 

Redis Desktop 

 

Fluent NHibernate Nedir ve Kullanımı

ORM Nedir ?

ORM (object relational mappers) uygulamalarınızda CRUD (Create, Read, Update, and Delete) işlemleri için basit bir şekilde data'ya erişimlerimizi sağlayan yapılardır. Çeşitli ORM framework'leri uzun zamandan beri data-model arasındaki veri alış verişini sağlamak için kullanılmakta ve ORM ler sql scriptleri execute etmeden CRUD işlemlerini doğrudan yapabilmemiz için kod yazmamıza olanak sağlarlar. Özetle, ORM kullanarak uygulamalarımızdaki data-model ilişkisini(database modellemesini) object-model ilişkisine dönüştürebiliriz.

Neden Fluent NHibernate ?

Klasık NHinernate, database de bulunan her bir tablo için class mapping bilgilerini .hbm uzantılı  XML formatındaki dosyalarda saklıyordu ve aslına bakılırsa biraz zahmetli bir işlemdi. Fluent NHibernate'de ise artık .hbm uzantılı mapping bilgilerini içeren XML formatındaki dosyalar kaldırıldı ve bu işlem code-behind'a taşındı ve diğer ORM'lere göre daha hızlı olduğu kabul edilir.

Kısaca özelliklerini sıralamak gerekirse;

  • Easy data-access and data-mapping,
  • Compile-time name- and type-safety,
  • IntelliSense to show you which fluent methods are available at any point,
  • Automapper impl.

Fluent NHibernate ile kolayca Linq sorguları yapabilir ve böylelikle Fluent bir Api oluşturmada kullanabiliriz. Şimdi ise projemize nasıl Fluent NHibernate entegre edip geliştirme yapabiliriz bunu incelicez.

Fluent NHibernate Kurulum

İlk olarak Visual Studio üzerinde yeni bir Console Application projesi açalım. Daha sonra Nuget üzerinden Fluent NHibernate referanslarını projemize indirip kuralım. Install-Package FluentNHibernate

Database Tablo Oluşturulması

Fluent NHibernate kullanabilmek için öncelikle bir database'e ihtiyacımız var. SQL üzerinde FNH_Db adında adında bir database'imiz olsun ve bu db'ye ait aşağıda bulunduğu gibi User adında bir tablo oluşturalım.

CREATE TABLE dbo.Users  
(
    Id int PRIMARY KEY NOT NULL,  
    Name varchar(25) NOT NULL,  
    SurName varchar(25) NOT NULL
)  

User Tablosuna Karşılık Gelen User.cs Oluşturulması

Yukarıda tanımlamış olduğumuz User tablosuna karşılık gelen User.cs aşağıdaki gibi oluşturuyoruz.

public class User
   {
       public virtual int Id { get; set; }
       public virtual string Name { get; set; }
       public virtual string SurName { get; set; }
   }

Mapping Class'ının Oluşturulması

Database tablomuz ve buna karşılık gelen class'ımız hazır ancak halen User objesinde bulunan hangi alan tabloda bulunan hangi alana karşılık geliyor bunu belirtmedik. Bunun için Fluent NHibernate'e ait olan ClassMap adında bir class bulunmakta ve gerekli mapping işlemlerini yapacağımız class ClassMap<T>'den inherit olmuş şekilde oluşturulacak. Burda ki T bizim User class'ımız oluyor.

UserMap.cs adındaki mapping class'ı aşağıdaki gibi olacak şekilde tanımlıyoruz.

   public class UserMap : ClassMap<User>
   {
       public UserMap()
       {
           Id(x => x.Id);
           Map(x => x.Name);
           Map(x => x.SurName);
           Table("User");
       }
   }

DB Connection Sağlanması

Şimdiki işlem ise Fluent Nhibernate kullanarak yukarıda tanımlamış olduğumuz FNH_Db isimli database'e bağlanmak. Bunun için OpenSession adında bir metot oluşturalım.

       public static ISession OpenSession()
       {
           string connectionString = "FNH_Db conn string";
           var sessionFactory = Fluently.Configure()
               .Database(MsSqlConfiguration.MsSql2012
                .ConnectionString(connectionString).ShowSql()
               )
               .Mappings(m =>m.FluentMappings.AddFromAssemblyOf<User>())
               .ExposeConfiguration(cfg => new SchemaExport(cfg)
               .Create(false, false))
               .BuildSessionFactory();
           return sessionFactory.OpenSession();
       }

Tablo'da Bulunan Verileri Alma

Aşağıda yer alan kod bloğu ise db de bulunan tablodaki verileri query etmemizi sağlar.

using (var session = OpenSession())
     {
          var users = session.Query<User>().ToList();
     }

Yukarıda bulunan geliştirmeleri yaptığınızda db bulunan bir proje geliştirmede basitçe connection sağlayarak data-model ilişkisini kurabilirsiniz.  

Not: Yazıyı basit tutmak adına herhangi bir design pattern den bahsetmedim ancak Db olan yerde Crud işlemleri vardır, Crud olan yerde Ado.Net veya ORM vardır, ORM varsa Fluent NHibernate vardır ve Fluent NHibernate de Repository Design Pattern ile birlikte fıstıklı baklava gibi gider :) 

Fluent NHibernate ile ilgili daha detaylı bilgi almak ve geliştirmeleri takip edebilmek için bu linke göz atabilirsiniz.

IComparable Interface'ini Kullanarak Sıralama İşlemi

Primitive type'lar için sıralama işlemini bir şekilde list.Sort() vs gibi metotlar kullanarak yapabiliyoruz. Peki kendi yazdığımız objeler için bunu nasıl yaparız ? .Net tarafında iki objeyi belli field&property'lere göre sıralamamızı veya compare etmemizi sağlayan IComparable<T> interface'i bulunmakta. Bu interface ile List<T> tipidne tanımladığımız değişkenimize bizim belirttiğimiz kritere göre sıralama özelliği kazandırmış oluyoruz. Örnek bir case üzerinden gidelim; Rectangle adında bir objemiz olsun içerisinde Rectangle objeleri bulunan bir array'imiz olsun bu arrayde bulunan objeleri alanı en küçük olandan büyük olana doğru sıralayalım. İlk olarak Rectangle objemizi aşağıdaki gibi tanımlayalım.

    public class Rectangle 
    {
        public double Length { get; set; }
        public double Breadth { get; set; }

        public double Area
        {
            get
            {
                return Length * Breadth;
            }
        }
    }

Şimdi ise üsttede belirtiğimiz gibi dikdörtgen objelerini küçük olandan büyük olana doğru array'de sıralayalım. Bu işlem yapabilmenin bir yolu IComparable interface'ini kullanmak. Rectangle objemizi bu interface'den implement etmek. Implementasyon sonucunda Rectangle class'ı CompareTo adında bir metota sahip olur. Bu metot aracılığıyla compare işlemi yaparak array'imizi sıralayabiliriz. Rectangle class'ımızın implementasyon sonrasındaki görünümü aşağıdaki gibi olacaktır.

    public class Rectangle : IComparable<Rectangle>
    {
        public double Length { get; set; }
        public double Breadth { get; set; }

        public double Area
        {
            get
            {
                return Length * Breadth;
            }
        }

        public int CompareTo(Rectangle other)
        {
            if (this.Area == other.Area)
            {
                return this.Area.CompareTo(other.Area);
            }
            return other.Area.CompareTo(this.Area);
        }
        public override string ToString()
        {
            return this.Area.ToString();
        }
    }

 Şimdi ise sırada yazdığımız bu kodu test etme işlemi var. 

        public static void Main()
        {
            var list = new List<Rectangle>
            {
                new Rectangle() {Length = 7, Breadth = 3},
                new Rectangle() {Length = 5, Breadth = 1},
                new Rectangle() {Length = 9, Breadth = 4},
                new Rectangle() {Length = 2, Breadth = 7},
                new Rectangle() {Length = 3, Breadth = 5}
            };

            list.Sort();//Sort metodu ile array'i belirtmiş olduğumuz kritere göre sıralıyoruz

            foreach (var element in list)
            {
                Console.WriteLine(element);
            }
        }

Projeyi çalıştırdığımızda aşağıdaki gibi bir ekran elde etmiş olacağız.

36
21
15
14
5