Advertise with Googlier.com .Net – Bilişim IO https://bilisim.io Yazılım, Mobil, Big Data, Yapay Zeka, Machine Learning, Bilim, Teknoloji, Haber, Makale, Tool, Tutorial, Video ve Etkinlik paylaşım platformu Wed, 27 May 2020 11:53:26 +0000 en-US hourly 1 https://wordpress.org/?v=7.1.1 https://bilisim.io/wp-content/uploads/2017/02/cropped-bilisim-io-profil-32x32.jpg .Net – Bilişim IO https://bilisim.io 32 32 .Net Core İle Pardus Üzerinde Uygulama Geliştirme https://bilisim.io/2020/05/27/net-core-ile-pardus-uzerinde-uygulama-gelistirme/ https://bilisim.io/2020/05/27/net-core-ile-pardus-uzerinde-uygulama-gelistirme/#respond Wed, 27 May 2020 11:53:26 +0000 https://bilisim.io/?p=7765 .Net Core İle Pardus Üzerinde Uygulama Geliştirme]]>

Merhaba,

Uzun bir aradan sonra Linux üzerindeki .Net Core Makalelerime devam etmeye karar verdim.
Genelde herkesin yaptığı gibi Ubuntu üzerinden anlatmaya devam edecektim. Fakat sonradan ülkemizde geliştirmesini yaptığımız
Pardus üzerinden bu konuyu anlatmak istedim. Açıkçası hepimizin açık kaynak kod sevdası olmakla birlikte
bu mecrada çalışan arkadaşlarımıza yazılımcılar olarak gerekli desteği vermediğimizi düşünüyorum.

Bundan dolayı da makalemi Pardus üzerinden anlatacağım. PARDUS, Debian GNU/Linux temelli Özgür ve Açık Kaynak kodlu bir işletim sistemidir. İnternet üzerinden ücretsiz olarak indirilebilmekte ve kurulabilmektedir.2003 yılında planlamış olup ilk kararlı sürümünü 2005 yılında çıkarmıştır. Pardus’a olum veya olumsuz bulanmaktadır. Fakat ben yukarıda belirttiğim gibi bu sistemleri biz kendimiz kullanmadıkça saha da yaygınlaştırmadıkça onun gerçek gelişimine katkı sağlayamayız.
Belki bizim burada yazacağımız makale ile Kurumsal Pardus kullanımları daha fazla artar ve geliştirme seviyemiz daha yüksek seviyelere ulaşır. Pardus’un kurumsal sitesindeki hedelerinden biri de aslında budur. Şimdiden Pardus üzerinde emeği geçen tüm arkadaşlarıma teşekkür ederim.
Ben Pardus işletim sistemini VmWare üzerine kurdum.
Sizler aşağıdaki seçeneklerden biri ile kurabilirsiniz.

  • VmWare üzerine kurmak için takip edilecek önergeler için tıklayınız.
  • Yeni bir kurulum olarak kurmak için takip edilecek önergeler için tıklayınız.

Pardus’un iso formatındaki dağıtımını https://www.pardus.org.tr/ sitesinden edinebilirsiniz.

Uygulamamızı bir efsane olan PHP+MySql ikilisini baz alarak yazmak istedim. Böylece .Net geliştiricileri de Linux ortamlarında bu ikili gibi yazılım geliştirebileceklerini görmelerini istedim.
Bundan sonra bize gerekli bileşenleri şu şekilde listeleye biliriz.
1. Visual Studio Code
2. .Net Core Sdk
3. Mysql/MariaDB

Visual Studio Code

Pardus Kurulumumuzu tamamladıysak bundan önceki Ubuntu makalesinde olduğu gibi Visual Studio Code uygulamamızı indirmemiz gerekiyor.
Buradaki makaleden bu indirme işlemini görebilirsiniz.

.Net Core Sdk

.Net Core Sdk tarafında ise henüz Pardus işletim sistemimiz çok yaygın olmadığı için direk göremeyeceksiniz. Umarım ileride bu işletim sistemimizi de bu listelerde direk görebilirsiniz.
Ayarları yaparken Debian 10 sürümünü baz alabilirsiniz. Ben aşağıda ayarları veriyor olacağım. Ama sistemler güncellendikçe Debian sürümü üzerinden ilerleyebilirsiniz.

Ubuntu da olduğu gibi Linux ortamına .Net Core kurmak için önce Microsoft Anahtarını repoya kayıt etmeliyiz. Daha sonra kurma ve güncelleme işlemlerini yapabilirsiniz.

Şimdi bir komut satırı açalım. Unutmayın komutları sırasıyla çalıştırmalısınız. Pardus Türkçelendirmesinde uçbirim öykünücüsü denmiştir.

Ben işlemleri rahat yapabilmek için root kullanıcısına geçiş yaptım. Aşağıdaki komut ile bunu yapabilirsiniz.

Microsoft Anahtarını repoya kayıt

wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor  microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget https://packages.microsoft.com/config/debian/10/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list

.NET Core 3.1 SDK Yükleme

sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-3.1

Install the ASP.NET Core runtime

sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install aspnetcore-runtime-3.1

Install the .NET Core runtime

sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-runtime-3.1

Mysql/MariaDB

Linux üzerinde işlem yapanlar ve php kullananlar için vazgeçilmez veritabanı MySqldi. Lakin Oracle’in satın almaları sonucunda eskiden düşük ücretle veya ücretsiz destek verilen bir çok ürünün desteği ücretli hale geldi. Bu ve bunun gibi bir çok dedi kodu sonucunda Maria DB Mysql baz alınarak daha doğrusu baz kodu üzerinden yeni bir dal başlatılarak geliştirilmeye başlandı. Genel olarak bildiğmiz ve kullandığımız MySql komut ve arayüzlerini kendisi içermektedir.
Kurulumu yapabilmek için tekrardan komut satırını açıyoruz. Daha sonra aşağıdaki kodu çalıştırmamız gerekmektedir.

sudo apt install default-mysql*

Kurulum tamamlandıktan sonra

sudo mariadb -u root

Uygulamamız için kendi uygulama kullanıcımızı oluşturmamız gerekiyor. root ile bağlanabilirmiydik evet ama disiplin ve güvenlik için yeni kullanıcı oluşturmalıyız. Demo yaparken bile bunları yaparsak alışkanlık edinmiş oluruz.

CREATE USER 'dbUser'@'localhost' IDENTIFIED BY 'parola';
GRANT ALL PRIVILEGES ON * . * TO 'dbUser'@'localhost';
FLUSH PRIVILEGES;
exit;

Hadi geliştirme işlemlerine başlayalım

Öncelikle visual studio code uygulamamızı açalım. Daha sonra terminal arayüzünü açarak işlemlerimize başlayalım.

terminal arayüzünde uygulamamızı oluşturmak istediğimiz dizine gidelim. Aşağıdaki komutu yazarak uygulama dizinini oluşturalım.

mkdir MyDbDemo

dotnet uygulamamızı oluşturalım

dotnet new console

uygulamamız oluştuktan sonra bize gerekli olan paketleri terminal üzerinden ekliyoruz.

dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.Tools.DotNet
dotnet add package Pomelo.EntityFrameworkCore.MySql

MariaDb’yi Entity Framework Core ile kolayca kullanabilmek için Pomelo nun paketinide sisteme ekliyoruz.

Öncelikle uygulamamıza StoreModel.cs ekledim. Entity sınıflarımızı bu kısma ekleyeceğiz. CodeFirst yaklaşımda olan sınıflarımız ile tabloların karşılıklarını bu şekilde oluşturmuş olacağız.

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace MyDbDemo
{
  public class Product
  {

    [Key]
    public int Id {get;set;}
    public string Barcode { get; set; }
    public string Title { get; set; }
    public int Price { get; set; }
    public virtual Category Category { get; set; }
  }
  public class Category
  {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; }
  }
}

Daha sonra uygulamamıza StoreContext.cs dosyasını ekliyorum. StoreContext DbContext ‘ten türemektedir. Burada önemli olan OnConfiguring methodunun override edilmesidir. Farklı veritabanlarına bağlanırken veya biz bir veritabanı bağlantı arabilirimi tanımlamak isterisek geliştirme yapılması gereken kısım burasıdır. Pomelo ‘nun kodlarını inceleyerek bu kısmı anlayabilirsiniz.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseMySql("server=localhost;database=store;user=user;password=password");
    }

using Microsoft.EntityFrameworkCore;
using Pomelo.EntityFrameworkCore.MySql.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyDbDemo
{
  public class StoreContext : DbContext
  {
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseMySql("server=localhost;database=store;user=user;password=password");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      base.OnModelCreating(modelBuilder);
      modelBuilder.Entity<Category>(entity =>
      {
        entity.HasKey(e => e.Id);
        entity.Property(e => e.Name).IsRequired();
      });
      modelBuilder.Entity<Product>(entity =>
      {
        entity.HasKey(e => e.Id);
        entity.Property(e => e.Title).IsRequired();
        entity.HasOne(d => d.Category)
          .WithMany(p => p.Products);
      });
    }
  }
}

Veritabanı erişimlerimiz ve nesnelerimiz hazır olduğuna göre artık veri yazma ve okuma işlemlerini yapabiliriz. Bu kısma kadar olan yerde göreceğiniz gibi klasik EF işlemlerini yaparak ilerledik.

Şimdi Program.cs dosyasına gidelim ve işlemlerimizi yapalım.

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace MyDbDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Sistem Başlıyor!");
            using (var context = new StoreContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
                CreateProduct(context);
                Console.WriteLine("Ürünler oluşturuldu!");

                ListProduct(context);
                Console.WriteLine("Ürünler Listelendi!");
                Console.WriteLine("Çıkmak için bir tuşa basınız");
                Console.ReadKey();
            }
        }
        private static void ListProduct(StoreContext context)
        {
           Console.WriteLine("Ürünler Listeleniyor.\n");
                var query = context.Products.ToList();

                foreach (var product in query)
                    Console.WriteLine("{0} | {1} | {2} | {3}", product.Id,
                    product.Title, product.Price, product.Category.Name);

        }
        private static void CreateProduct(StoreContext context)
        {
            var homeCategory = new Category()
                {
                    Name = "Ev Ürünleri"
                };
                var enterpriseCategory = new Category()
                {
                    Name = "Kurumsal Ürünler"
                };

                context.Categories.Add(homeCategory);
                context.Categories.Add(enterpriseCategory);

                context.Products.Add(new Product()
                {
                    Title = "Fritöz",
                    Price = 100,
                   Barcode="1115",
                    Category = homeCategory
                });
                context.Products.Add(new Product()
                {
                    Title = "Matkap",
                    Price = 1000,
                    Barcode="1111111",
                    Category = enterpriseCategory
                });
                context.SaveChanges();
        }
    }
}

Uygulamamızı tamamladıktan sonra yine terminalden aşağıdaki kod ile çalıştırabiliriz. Çıktısı aşağıdaki gibi olacaktır.

dotnet run

Uygulamamızın çıktısı yukarıdaki olacaktır. Kendi geliştirmemiz olan Pardus işletim sistemi üzerinde ufakta olsa geliştirmenin ne kadar kolay yapılabildiğini görmüş olduk.
Bu uygulamamız üzerine klasik loglama ve yine veritabanı sql loglarını oluşturabileceğiniz arabirimlerini ekleyerek uygulamayı genişletebilirsiniz.
Sağlıcakla kalın.

]]>
https://bilisim.io/2020/05/27/net-core-ile-pardus-uzerinde-uygulama-gelistirme/feed/ 0
ASP.NET Core Dependency Injection https://bilisim.io/2020/05/06/asp-net-core-dependency-injection/ https://bilisim.io/2020/05/06/asp-net-core-dependency-injection/#comments Tue, 05 May 2020 23:55:33 +0000 https://bilisim.io/?p=7599 Bu yazıda, son yılların popüler teknik terimlerinden olan Dependency Injection kavramını ASP.NET Core üzerinde detaylı örneklerle anlatmaya çalışacağım. Kısaca DI olarak adlandırdığımız bu yapı Core çatı framework'ün temel ilkelerinden birisidir. ]]>

Merhaba,

Bu yazıda, son yılların popüler teknik terimlerinden olan Dependency Injection kavramını
ASP.NET Core üzerinde detaylı örneklerle anlatmaya çalışacağım.

Makale İçeriği

  • Loose Coupling yaklaşımı ile refactoring
  • DI Constructor Injection
  • DI Container ile Service registration
  • Dependecy Injection
  • Inversion Of Control ( IoC )
  • Dependency Inversion Principle
  • xUnit Test senaryosu

Dependency Injection Nedir ?

Kısaca DI olarak adlandırdığımız bu yapı NetCore framework’ün temel ilkelerinden birisidir.  Türkçemize “Bağımlılık Enjeksiyonu”  adıyla  çevrilmiştir.

Asıl anlatılmak istenen ise; farklı nesnelerin birbirleriyle olan bağlılıklarını daha esnek bir yapıda ele alıp oluşabilecek yazılım maliyeti ve karmaşanın önüne geçmektir.
Dependency Injection tasarım deseni iki temel prensip üzerine kurulmuştur.

  1. Inversion Of Control ( IoC )
  2. Dependency Inversion Principle

Inversion Of Control

Inversion Of Control ( IoC ) ;  Temel olarak uygulamanızda nesne oluşturma aşamalarının sizin yerinize framework’ün yönettiği yapıdır.  IoC genel bir kavramdır ve nesnelerin farklı tekniklerle Inject edilebilmelerine olanak sağlar.
Dependency Injection ise IoC’nin uygulama yöntemlerinden biridir. IoC tarafında ayaklandırılmış nesnenin  nereye inject edileceğine nokta atışı karar verilen yapıdır.

 

 

Neden Dependency Injection Kullanılır ?

Problem1 : Bağlılık Yapısı
Problem2 : Test Edilebilirlik

Proje büyüdükçe maliyet ve karmaşa artar bu nedenle yapılar olabildiğince sade, okunaklı ve anlaşılabilir hazırlanmalıdır. Yaşam döngüsü boyunca bir çok geliştirme hatta yapısal değişikliğe uğrayan projelerde kurduğumuz yapıların değişikliklerden minimum ölçüde etkilenmesi  gerekir. Bahsedilen bu hedefi yakalayabilmek için  loosely coupled ( Esnek bağlılık ) yaklaşımını sergileyebilmek önemli, işte tam bu noktada Dependency Injection pattern ihtiyaça yönelik yaklaşımı sunar.
Ek olarak uzun vadede Bug Fix maliyetlerinin azaltılmasında önemli rol oynayan test senaryolarının doğru ve efektif yazılabilmesi için de önemli ölçüde  DI ihtiyacı vardır.

Problemin Tanımlanması

 

Örnek Senaryo ( iş kuralları için kodlama yapılmamıştır)
Index sayfasında Ziyaretçiler için Ürün önermesi ( ProductSuggestion ) yapılacak.

Ürün önerisinde bulunacak sınıfı aşağıdaki gibi hazırladım.

public class GuestProductSuggestion
 {
        public ProductSuggestionResponse GetProductSuggestion()
        {
            // logic 
            return new ProductSuggestionResponse 
            { 
               Message = "Guest Suggestion",
               ProductList = new List<string> {  "Klavye", "Kulaklık" } 
            };
        }
 }

 

HomeController/Index
Anasayfada ürün önerimizi gösterebilmek için Index action methodunu tanımlayalım

public class HomeController : Controller
  {
     
        public IActionResult Index()
        {
            var guestProductSuggestion = new GuestProductSuggestion();
            var suggestionResponse = guestProductSuggestion.GetProductSuggestion();

            return View(new IndexModel { ProductSuggestion = suggestionResponse });
        }
   }

Yukarıdaki kod blogunu incelediğimizde HotelController sınıfının Index methodunda GuestProductSuggestion sınıfını new keyword ile oluşturduğunu görüyoruz. HomeController GuestProductSuggestion sınıfından instance almayla sorumlu olduğuna göre aralarında                 tam bağımlılık oluşmuş durumdadır.

 

Örnek Senaryo için Ek Geliştirme ; Index sayfasında Kayıtlı kullanıcı için farklı iş kuralları ve ürün önermesi oluşturulmalıdır.

 

Örnek senaryomuza yeni bir gelitşirme eklendi, kayıtlı kullanıcılar için farklı ürün önermeleri yapılacak. Bahsedilen kullanıcılarr için öneri dönüşü yapan yeni bir sınıf oluşturmamız gerekmektedir.

FuneralCoffinDance

 

( Eğer bu aşamada soyutlama yerine if koşullandırması veya parametre ile ayrıştırma yoluna giderseniz ilerde sizi iyi şeyler beklemiyor 🙂    >>>>

 

 

 

Aşağıdaki gibi UserProductSuggestion sınıfını oluşturalım.

public class UserProductSuggestion
 {
        public ProductSuggestionResponse GetProductSuggestion()
        {
            // logic 
            return new ProductSuggestionResponse 
            { 
               Message = "User Suggestion",
               ProductList = new List<string> { "4K Android TV", "MAC Book PRO" } 
            };
        }
 }

 

HomeController Index methodununu hatırlayalım. Kayıtlı kullanıcılar için geçerli bir tahminleme yapmak istiyorsak aşağıdaki kod bloğunu değiştirmeniz gerekiyor ve bu değişiklik farklı kod bloklarını da etkileyebilir.. İşte bu karmaşa bizim ilk problemimiz bkz;
[ Problem1: Bağlılık Yapısı ]

// var guestProductSuggestion = new GuestProductSuggestion(); 
// var suggestionResponse = guestProductSuggestion.GetProductSuggestion();

var userProductSuggestion = new UserProductSuggestion();
var suggestionResponse = userProductSuggestion.GetProductSuggestion();

 

Şimdi ikinci problemi tanıyalım, Ürün önerme Testleri için 2 farklı senaryo hazırladım. ( Test işlemleri için xUnit kullanılmıştır )

 

1. Test senaryosu ; ürün önerisinin olduğu senaryoyu test ediyor.

[Fact]
  public void IndexExpected_AvailableGuestProductSuggestion()
  {
     var controller = new HomeController();
     var actionMethod = controller.Index();
     var actionResultView = Assert.IsType<ViewResult>(actionMethod);
     var resultModel = Assert.IsAssignableFrom<IndexModel>(actionResultView.Model);

     var availableProductSuggestion = resultModel.ProductSuggestion.ProductList.Count > 0;

     Assert.True(availableProductSuggestion);
  }

2. Test senaryosu ; Herhangi bir ürün önerisinin olmadığı senaryoyu test ediyor.

[Fact]
  public void IndexExpected_NOT_AvailableGuestProductSuggestion()
  {
      var controller = new HomeController();
      var actionMethod = controller.Index();
      var actionResultView = Assert.IsType<ViewResult>(actionMethod);
      var resultModel = Assert.IsAssignableFrom<IndexModel>(actionResultView.Model);

      var NotAvailableProductSuggestion = resultModel.ProductSuggestion.ProductList.Count == 0;

      Assert.True(NotAvailableProductSuggestion);
  }

 

Testleri çalıştırdığımızda 1. Test senaryosunun başarılı olduğunu görüyoruz. 2. Test ise hata veriyor.
Methodu biraz daha açıklamaya çalışayım ;

  • Home Controller üzerinden instance oluşturuldu.
  • Index methodu Invoke edildi.
  • Methodunda sonucu return değerine ulaşıldı. [ Core.DI.Model.IndexModel ]
  • Model dönüşü üzerinden test result oluşturuluyor

 

Ürün önerisinin başarılı olmadığı test senaryosunu inceleyelim;

Hatırlatma için Index action kodlarını ekledim. GuestProductSuggestion nesnesinden instance almak ve yönetmekten sorumlu olduğundan HomeController > Index ile GuestProductSuggestion  arasında tam bağımlılık vardı

public IActionResult Index() 
{ 
   var guestProductSuggestion = new GuestProductSuggestion(); 
   var suggestionResponse = guestProductSuggestion.GetProductSuggestion(); 
   return View(new IndexModel 
   { 
       ProductSuggestion = suggestionResponse 
   });
}

 

Tam bağımlılık nedeniyle test için uygulanan senaryoların tamamında GetProductSuggestion methodu aynı değeri döndürecektir. ( ProductList = new List<string> { “Klavye”, “Kulaklık” } )  işte bu durum bizim ikinci problemimizi ortaya koyuyor bkz;
[ Problem2 : Test Edilebilirlik ]

Çözümü Tasarlayalım

 

Problemi tanımlayabildiğimize göre artık çözüm yoluna geçebiliriz. Kod tasarımında bazı değişiklikler yapmalıyız. Loose Coupling yaklaşımı ile tam bağımlılıkları ortadan kaldıracağız. Yüksek seviyedeki sınıfların düşük seviyedeki sınıflara olan bağlılığını ters bağlılığa çevireceğiz. Bunun için soyutlaştırma kullanacağız ve constructor Injection dan yararlanacağız. Inversion Of Control ile dışarıdan Inject edilen sınıfların instance yönetimini uygulamamızdan çıkartıp, üst seviyede ele alacağız. ( Service Registration )

 

Dependency Injection Tersine Bağlılık

 

Dependency Inversion Principle; Yüksek seviyeli sınıfların düşük seviyeli sınıflara direk bağımlı olmadan yönetilmesini hedefleyen prensiptir. Problemin karşılaşıldığı yapılarda bağımlılığın soyutlandırma ile tersine çevirilmesi şeklinde yorumlanabilir.

 

Problem1: Bağlılık Yapısı

Öncelikle HomeController sınıfının GuestProductSuggestion sınıfına bağımlılığını ortadan kaldıralım.
Soyutlandırma yönteminden faydalanacağımız için GuestProductSuggestion sınıfını Interface ile dışarıya açmamız gerekiyor.

IProductSuggestion inteface’ini oluşturalım.

public interface IProductSuggestion
 {
    ProductSuggestionResponse GetProductSuggestion();
 }

 

Ürün önerisini yapacak olan sınıflarrımızı IProductSuggestion Interface’inden türetelim.

public class UserProductSuggestion : IProductSuggestion

 public class GuestProductSuggestion : IProductSuggestion

 

Artık HomeController’ı bağlılıktan kurtarmanın vakti geldi.Dependency Injection ile HomeController’a IProductSuggestion’ı inject edelim ve Index Action’ı düzenleyelim.

 

private readonly IProductSuggestion _productSuggestion;

 public HomeController(IProductSuggestion productSuggestion)
 {
     _productSuggestion = productSuggestion;
 }

 public IActionResult Index()
 {
     var suggestionResponse = _productSuggestion.GetProductSuggestion();
     return View(new IndexModel { ProductSuggestion = suggestionResponse });
 }

Index action methoduna bakacak olursak new ile Instance almadığımızı Dependency Injection üzerinden Constructor Injection ile gelen değeri kullandığımızı görebilirsiniz.

Inversion Of Control ile IProductSuggestion interface’imizi uygulamanın kullanabilmesi için hazır bir instance haline getirelim.

Startup.cs > ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
     services.AddTransient<IProductSuggestion, GuestProductSuggestion>();
}

 

Problem2: Test  Edilebilirlik

Test için yazdığımız kodlarda ufak değişiklikler yapacağız.  Daha önce hata veren test methodumuzu aşağıdaki gibi değiştirdim.
Bağlılığımız dışarıdan Inject edildiği için Interface üzerinde Mock ile Controller test senaryoma uygun örneği oluşturabiliyorum.

public void IndexExpected_NOT_AvailableProductSuggestion()
 {
    var mockHomeController = new Mock<IProductSuggestion>();
    mockHomeController.Setup(p => p.GetProductSuggestion())
                      .Returns(new ProductSuggestionResponse()
    {
        Message = "Guest Suggestion",
        ProductList = new List<string>()
    });
    var controller = new HomeController(mockHomeController.Object);
    var actionMethod = controller.Index();
    var actionResultView = Assert.IsType<ViewResult>(actionMethod);
    var resultModel = Assert.IsAssignableFrom<IndexModel>(actionResultView.Model);

    var NotAvailableProductSuggestion = resultModel.ProductSuggestion.ProductList.Count == 0;

    Assert.True(NotAvailableProductSuggestion);
 }

 

Umarım faydalı bir yazı olmuştur.

 

 

 

 

Inversion Of Control Discussion
Inversion Of Control Matin Fowler Article

 

 

 

 

]]>
https://bilisim.io/2020/05/06/asp-net-core-dependency-injection/feed/ 2
Clean Code [ Temiz Kodlama ] Prensipleri – Method Function https://bilisim.io/2020/04/25/clean-code-method-principles/ https://bilisim.io/2020/04/25/clean-code-method-principles/#respond Sat, 25 Apr 2020 16:46:59 +0000 https://bilisim.io/?p=7493 Clean Code RefactoringBu yazıda kodlama alışkanlıklarımızı gözden geçirmemize sebep olabilecek bazı detaylardan bahsedeceğim. Yazılımcı olarak bir projeye sıfırdan başlar veya T anında dahil oluruz. Proje büyüdükçe birbirini takip eden iş kuralları da büyür ve iç içe girmeye başlar. Hatta bazı iş kurallarına analizde değil direk kodlamada karşılaşırız. Bahsettiğim bu son durum bütünün farklı sorunları olarak ele alınsa da gelmek istediğim noktada, kod yazarken meslektaşlarımıza ipuçları bırakmanın öneminden açık ve temiz kod yazmanın faydalarından bahsediyor olacağım. Bu yazıdaki örnekleri incelerken refactoring esnasında Temiz Kod Clean Code prensiplerinden faydalanıp sadeleştirdiğiniz kod bloklarını yorumlarda paylaşabilirsiniz :)]]> Clean Code Refactoring

Merhaba ,

Eğer okumadıysanız bu serinin ilk yazısı olan Clean Code [ Temiz Kodlama ] Prensipleri – Giriş ile başlamanızı tavsiye ederim.

Önceki yazımızda Temiz Kodlama prensipleri üzerinde genel hatlarıyla tecrübeler edinmiştik , şimdi biraz daha spesifik bir konu özelinde değerlendirme yapacağız.  “METHODS – FUNCTIONS”

Rule Of 7


       İnsanlar kısa süreli bellekte sadece 7 maddeyi tutabilirler.Temiz kod yazarken bu kuralı unutmayalım.

Değişkenler

Methodlarda değişken tanımlamalarımızı Just-in-time yani ilk değer atamasından hemen önce yapılmalı.

//yanlış
int minUserNameLenght = 6; 
int maxUserNameLenght = 20;
bool isAlphaNumeric;

if(userName.Length < minUserNameLength) 
 return false;  
if(userName.Length > maxUserNameLength) 
 return false; 
isAlphaNumeric = userName.All(Char.IsLetterOrDigit);

------------------------------------------------------------
// doğru
int minUserNameLenght = 6; 
if(userName.Length < minUserNameLength) 
 return false;  

int maxUserNameLenght = 20;
if(userName.Length > maxUserNameLength) 
 return false; 

bool isAlphaNumeric = userName.All(Char.IsLetterOrDigit);

 

Parametreler

Methodlarımızın parametre sayılarından bir şeylerin doğru veya yanlış gittiğini anlayabiliriz.  Fazla parametre alan methodlarımız parçalanmalı veya parametre olarak Model almalıdır.

Aşağıdaki örnekte kullanıcı kaydetme işlemi içinde sendEmail , sendBill gibi parametrelerin olması aslında bu method’un doğru kurgulanmadığına işaret. Method hem kullanıcı kaydedip hem de mail gönderme işlemini yapıyorsa Single Reponsible değildir. Birden fazla sorumluluğu olan methodlar parçalanmalıdır.

public void SaveUser(string userName, 
                     string password, 
                     string email, 
                     bool sendEmail, 
                     bool sendBill,
                     bool printReport)
{
   
}

 

Methodlara kısa bir gitiş yaptıktan sonra İlk önce bir method oluşturmakdaki amaçlarımızı sıralayalım. Aslında bu yazdıklarımız Temiz Kod yazımındaki prensiplerimiz yerine de geçebilir. Bu maddeler ile method refactor için doğru bakış açısını yakalayabiliriz.

  • Tekrarlamayı azaltmak
    • Kendini tekrar etme (DRY – Don’t Repeat Yourself ) prensibini düşünürsek , methodlar birbirini tekrar eden kod parçacıklarını tekilleştirmek için kullanılır.
  • Karmaşıklık ve Girintiler azaltmak
    • Kod bloğu ilk bakışta size karmaşık geliyorsa  ve iç içe bir den fazla if blogu varsa.  Tam beklediğimiz method refactor blokları.
      if()
      {
        if()
        {
           if()
           {
           }
        } 
      }
  • Belirsiz niyetlerden kurtul
    • Bazı kod bloklarında yazılımcı için en önemli ipucu method isimleri olur. Niyetin belli olmadığı yerlerde kod method üzerinden işlenebilir.
  • 1 den fazla işlem varsa bölümlendir
    • Tek sorumluluk ( Single Responsibility ) prensibiyle method tek işlem özelinde değerlendirilmelidir. Bu nedenle birden fazla işlemin olduğu methodlar ve kod bloklarını düzeltilmelidir.

 

Method oluşturmak için elimizde 4 farklı madde var, bu maddelerden elde ettiğimiz çıkarımları Temiz kodlama ile birleştirmeye çalışalım. Detaylandırdığımız maddeleri aşağıda inceleyebilirsiniz.

Tekrarlamayı azaltmak 


Bu maddede DRY prensibinden bahsetmiştik. Kod tekrarlamanın yanına kod kopyalamayı da eklemek istiyorum. Copy – Paste sizi tembelliğe itecektir. Bir problemi birden fazla çözüm yolu vardır.  Copy – Paste ile kod tekrarlamak farklı düşünme alternatiflerinizi çöpe atmak demektir.
Genel anlamda kod tekrarlarınızın projemide etkisi ;

  • Okunabilirliği azaltır
  • Kodunuzu daha kompleks hale getirir
  • Hata yapma olasılığınızı arttırır ve bugFix maliyetiniz artar.
  • Bakım (maintance) maliyetiniz artar

 

Karmaşıklık ve Girintiler

 

Yukarıda başlığımızı kısaca açıklamıştık. Şimdi de karmaşıklık ve girinlilere neden olabilecek kod bloklarını Clean-Code prensibiyle çözümleyeceğiz. Ana hedefimiz girintiyi azaltmak

  1.  Exctract Method

    Bu yöntemde önceliğimiz kod bloğunu anlaşılabilir hale getirene kadar methodlara bölmek.

    if()
    {
       if()
       {
           do
           {
              // 
              //  kodlar
              //
           } while()
       }
    }

    if()
    {
       if()
       {
          DoSomething();
       }
    }
    
    
    private void DoSomething()
    {
       do 
       { 
          // 
          // kodlar 
          // 
       } while()
    }

     

  2. Fail Fast

    Kodumuzda hata fırlatıcaksak bunu ana kod bloğundan önce yapmalıyız.  Önce hataları handle et daha sonra iş yapacak kodu yaz.

    public void Login(string userName, string password)
    {
       if(!string.IsNullOrWhiteSpace(userName))
       { 
          // kod
          if(!string.IsNullOrWhiteSpace(password))
          {
             // Login
          }
          else 
          {
             throw ...
          }
       }
       else 
       {
              throw ...
       }
    
    }

    public void Login(string userName, string password) 
    { 
       if(string.IsNullOrWhiteSpace(userName)) 
           throw ...;
       if(string.IsNullOrWhiteSpace(password)) 
           throw ...;
    
       // Login;
    }

    Yukarıdaki kodlarda Fail Fast yöntemiyle girintileri minimuma indirdik, okunabilirliği arttırmış olduk.

  3. Return Early

    Fail Fast mantığıyla aynı düşünebiliriz. Fail Fast kazanımlarının hepsi geçerlidir. Methodun işleyişine göre return edilebilecek değerlerin öncelikli olarak geri dönülmesi ile uygulanır.

    private bool ValidUserName(string userName)
    {
       int minUserNameLenght = 6;
       int maxUserNameLenght = 20;
       bool isValid = false;
       
       if(userName.Length >= minUserNameLength)
       {
            if(userName.Length <= maxUserNameLength)
            {
                bool isAlphaNumeric = userName.All(Char.IsLetterOrDigit);
                if(isAlphaNumeric)
                {
                    if(!ContainsCurseWords(userName))
                    {
                       isValid = IsUniqueUserName(userName);
                    }
                }
            }
       }
       return isValid;
    }

    private bool ValidUserName(string userName) 
    { 
       int minUserNameLenght = 6; 
       if(userName.Length < minUserNameLength) 
          return false;
       int maxUserNameLenght = 20; 
       if(userName.Length > maxUserNameLength) 
          return false;
       bool isAlphaNumeric = userName.All(Char.IsLetterOrDigit); 
       if(!isAlphaNumeric) 
          return false;
      
       if(ContainsCurseWords(userName)) 
          return false;
    
       return IsUniqueUserName(userName);
    }

     

 

 

 

]]>
https://bilisim.io/2020/04/25/clean-code-method-principles/feed/ 0
Clean Code [ Temiz Kodlama ] Prensipleri – Giriş https://bilisim.io/2020/04/20/clean-code-principles/ https://bilisim.io/2020/04/20/clean-code-principles/#respond Mon, 20 Apr 2020 09:12:12 +0000 https://bilisim.io/?p=7403 Clean Code RefactoringBu yazıda kodlama alışkanlıklarımızı gözden geçirmemize sebep olabilecek bazı detaylardan bahsedeceğim. Yazılımcı olarak bir projeye sıfırdan başlar veya T anında dahil oluruz. Proje büyüdükçe birbirini takip eden iş kuralları da büyür ve iç içe girmeye başlar. Hatta bazı iş kurallarına analizde değil direk kodlamada karşılaşırız. Bahsettiğim bu son durum bütünün farklı sorunları olarak ele alınsa da gelmek istediğim noktada, kod yazarken meslektaşlarımıza ipuçları bırakmanın öneminden açık ve temiz kod yazmanın faydalarından bahsediyor olacağım. Bu yazıdaki örnekleri incelerken refactoring esnasında Temiz Kod Clean Code prensiplerinden faydalanıp sadeleştirdiğiniz kod bloklarını yorumlarda paylaşabilirsiniz :)]]> Clean Code Refactoring

Merhaba ,

Bu yazıda kodlama alışkanlıklarımızı gözden geçirmemize sebep olabilecek bazı detaylardan bahsedeceğim. Yazılımcı olarak bir projeye sıfırdan başlar veya T anında dahil oluruz. Proje büyüdükçe birbirini takip eden iş kuralları da büyür ve iç içe girmeye başlar. Hatta bazı iş kurallarına analizde değil direk kodlamada karşılaşırız. Bahsettiğim bu son durum bütünün farklı sorunları olarak ele alınsa da gelmek istediğim noktada, kod yazarken meslektaşlarımıza ipuçları bırakmanın öneminden açık ve temiz kod yazmanın faydalarından bahsediyor olacağım. Bu yazıdaki örnekleri incelerken refactoring esnasında Temiz Kod ( Clean Code ) prensiplerinden faydalanıp sadeleştirdiğiniz kod bloklarını yorumlarda paylaşabilirsiniz 🙂

Her boş insan bilgisayarın anlayabileceği kodu yazar.Sadece iyi yazılımcılar insanların anlayabileceği kodu yazar.
Martin Fowler

Temiz Kod ( Clean Code ) refactoring ‘e başlamadan genel hatlarıyla üç prensip üzerinde durmalıyız.

  • Doğru Araç kullanımı
    • Yazılım geliştirdiğimiz IDE,  Framework , Paket , güncel teknolojilerden faydalanmak
  • Kötü Gürültü sinyallerinin azaltılması
    • Aracınızda yolda giderken radyoda çalan şarkıya eşlik ediyorsunuz. Araç dağlık bir  bölgeden geçerken radyodan gelen cızırtılı ses’in hissettirdikleri bu madde olmalı 🙂
  • Dökümantasyon
    • Bilge bir programcı şöyle diyor , Kod level dökümantasyon için comment kullanmaya gerek yok, doğru yazılmış kod yeterli.

 

 

 

Boolean Karşılaştırmalar

Bool değişkenlerin karşılaştırılmasında sadelik ve okunablirlik ön planda olmalıdır.İki ifadeyi de okuyacak olursa ,  İlk İfadmizde  “Eğer done == true” ise  ikincide ifademizde “Eğer done” ise şeklinde okuyoruz. O halde en kısa şekilde ifade etmeliyiz.

 

if(done == true)

Accepted

if(done)

Boolean Değer Atamaları

Clean Code prensibinde ” Increase signal to noise ratio ” bloklarını azaltmamız gerekiyor.
İlk kod bloguna baktığımızda true ve false değerleri göze çarpıyor hemen ardından hasQualifyCampaign değişkeni için iki farklı şart olmalı diye kodu okumaya devam ediyoruz. True ve false değerlerini sinyal olarak ele alırsak hasQualifyCampaign  için iki adet sinyal gözden geçiriliyor.

İkinici kod bloğunda durumun böyle olmadığı ve tek sinyal algıladığımızı fark etmişsinizdir.  Okunaklı olması da tercih sebeplerini arttırıyor.

 

bool hasQualifyCampaign; 
if(totalWeeklySpend > 500)
{ 
   hasQualifyCampaign = true; 
} 
else 
{ 
   hasQualifyCampaign = false; 
}

bool hasQualifyCampaign = totalWeeklySpend > 500;

 

Pozitif Ol

Pozitif ifadeleri anlamak, işlemek ve yönetmek negatiflere göre çok daha basit ve işlevseldir.
Aşağıdaki örnekleri inceleyip negatif isimlendirilmiş değişkenler veya negatif bir sonuçtan pozitif çıkarım elde edilmiş değişkenlerde nasıl zorlandığınızı fark edeceksiniz. Pozitif olmak iyidir : )

 

if(!isNotLogIn)

if(isLogIn)

 

 

public bool IsValid()
{
     return !exceptions.Any();
}

public bool IsValid() 
{ 
  return exceptions.Count == 0; 
}

 

Ternary IF

Yine ” Increase signal to noise ratio ” ile karşı karşıyayız. Üstelik  dailyMessageLimit değişkeninin kullanım sayısı da dikkatinizi çekmiştir. 9 satır kod ve dönüş değerimiz 3 kez kullanılmış. Kodun sade ve tek satırda işlenmiş halinde çok daha anlaşılır olduğunu görebiliriz.

 

int dailyMessageLimit;
if(freeMember)
{
    dailyMessageLimit = 10;
}
else
{
   dailyMessageLimit = 100;
}

int dailyMessageLimit = freeMember ? 10 : 100;

 

Strongly type  kullan, stringly type değil 🙂

Aslında başlık her şeyi açıklıyor. Başlık haricinde Strongly Type ile;
Reference üzerinden değişken takibiniz kolaylaşır.
Code lens kullanımı kolaylaştırır.
Atamalarda ve eşitliklerde hata yapma olasılığınız yoktur.
Karşılaştırma yaparken Equals OrdinalCase, ToLower gibi önlemleri düşünmek zorıunda kalmazsınız.

 

if(productType == "HOTEL"

if(productType == ProductType.Hotel)

 

Başı boş ifadelerden kaçının

İlk örneğimizde aklımıza şöyle bir soru geliyor 16 ne ola ki ?
İkinci örneğimizde 16 sayısı net bir şekilde ifade edilmiş.Koşulun amacına yönelik çıkarımlar da yapabilir durumdayız.

 

if(age > 16)

int maxChildAge = 16; 
if(age > maxChildAge)

 

Karmaşık koşulları sadeleştir ve anlamlandır

İlk örneğimiz “Anlamlandırma” ile ilgili ; belirli bir şarta göre girilen if bloğunda bir güzellik yapılacak.
İşte o güzelliği ne için yaptığımızı anlamak için hem şartı hem de scope içindeki kodları gözden geçirmeliyiz.Daha sade yazılmış örneğini incelediğimizde promosyona hak kazanmış bir kullanıcıdan bahsedildiğini rahatlıkla anlayabiliriz.

 

if((DateTime.Now.Year - lastVisitDate.Year) <= 2 && childCount > 0)
{
   // yap bir güzellik 
}

bool acceptedPromotionGoal = (DateTime.Now.Year - lastVisitDate.Year) <= 2 && childCount > 0; 
if(acceptedPromotionGoal)
{
}

 

Açıklayıcı ol doğru aracı kullan.Güldürürken düşündürme, Güldür!

İki kod bloğunu incelediğimizde ilk bloğun çok yorucu olduğunu fark etmişsinizdir.

Kod 1
relatedContracts listesi tanımlıyoruz.
Eldeki contracts listesini dönüyoruz
Ödemesi tamamlanmış, Aktif  ve geçerli kontratı buluyoruz
relatedContracts’a ekliyoruz.
Listedeki tüm kayıtları döndükten sonra relatedContracts değişkenini return  ediyoruz

Kod 2

contracts listesinden Ödemesi tamamlanmış, Aktif  ve geçerli olanları geri dön

Ulaşmak istediğimiz hedefi olabildiğinde hızlı ve net bir şekilde anlatmamız ve açıklayıcı olmamız gerekiyor. Güldürürken düşündürmüyoruz 🙂 Diğer bir konu ise doğru araçların kullanılması.
ikinci bloğun yazılabilmesi LinqToObject ile mümkün. O halde doğru araçları kullanmalıyız

 

List<Contract> relatedContracts = new List<Contract>();

foreach(var contract in contracts)
{
    if(contract.IsActive && 
       contract.Status == ContractStatus.Done && 
       contract.RemainingPayment == 0)
    {
       relatedContracts.Add(contract);
    }
}

return relatedContracts ;

return contracts.Where(c => c.IsActive && c.Status == ContractStatus.Done)
                .Where(c => c.RemainingPayment == 0);

 

Referans

Cory House / Clean Code Principles

]]>
https://bilisim.io/2020/04/20/clean-code-principles/feed/ 0
Visual Studio Post-build script hazırlanması ve Jenkins Deployu https://bilisim.io/2020/02/27/visual-studio-post-build-script-hazirlanmasi-ve-jenkins-deployu/ https://bilisim.io/2020/02/27/visual-studio-post-build-script-hazirlanmasi-ve-jenkins-deployu/#respond Wed, 26 Feb 2020 21:04:37 +0000 https://bilisim.io/?p=7126 Bu makalede Visual Studio Post-build script hazırlamak ve Jenkins üzerinde Post-build deployunda karşılaşılabileceğiniz spesifik hatanın çözümünü inceleyeceğiz.]]>

 

Bu makalede Visual Studio üzerinde tanımladığımız Build-Event komutlarının Jenkins üzerinden başarılı bir şekilde build edilmesine örnekleme yapacağız.

Makalede Jenkins konfigürasyon, Jenkins deploy ve Job hazırlanması gibi konulara girmeden Jenkins continuous integration sürecinde projede kullandığınız Build-Event shell komutlarının desteklenmesi örneklemeye çalışacağım.

Problemin Jenkins üzerinden çözümüne gelmeden önce problemi oluşturan senaryonun oluşma aşaması ve detaylarından bahsetmek istiyorum.

Visual studio build işlemini MS-Build (Microsoft Build Engine) platformu üzerinden yapmaktadır. Projelerde build sonrası otomatik olarak yapılmasını istediğimiz bazı işlemleri project Properties altında Builds EventsPost-Build Events üzerinden kolayca çözümleyebiliriz.

Projemizdeki bir ihtiyaç ile başarıyla tamamlanan bir build sonrasında spesifik birkaç dll’in belirli bir yol içerisine kopyalanması için bazı Shell komutları yazmıştık.

Post-build

Yukarıda Post-build event command line içerisine yazılan shell komutları özetle projenin başarılı Build sonrasında $(SolutionDir) ve $(TargetDir) parametreleri üzerinden bir dll alışverişi yapmasını sağlıyor

Komutların yazılmasında kullanılar $(SolutionDir) , $(TargetDir) gibi parametreler Macros’lar dan temin ediliyor. (Edit Post-build > Macros )

Common macros for MSBuild commands and properties. bağlantısından MSBuild Macroslarını detaylı inceleyebilirsiniz.

Macros

Şimdi problem kısmına yoğunlaşabiliriz. Localde projenin build / Run aşamalarında herhangi bir problem olmamasına rağmen Jenkins deployunu tamamlayamadı. Jenkins loglarını incelediğimde problemin Post-Build scriptlerinde kullanılan değişkenlerle alakalı olduğunu farkettim.

Hem Jenkins hem de Visual Studio .Net projelerinde build işlemi için MSBuild kullanıyor.Aynı Build tool ile Visual Studio’nun başarılı bir deploy tamamlayıp Jenkins ‘in hata vermesinin nedeni ise ;  Build event script içerisinde kullanılan değişkenlerin Visual Studio tarafından MSBuild’e otomatik olarak gönderilmesi Jenkins tarafında ise manuel tanımlamaya gerek duymasıdır.

İlgili depoyu tamamlayan Jenkins Job üzerinde ek parametre tanımıyla başarılı deploy almaya devam edebilirsiniz. Benim ihtiyacımda SolutionDir key ‘in değerini Workspace ile alabiliyordum. Siz probleme göre farklı parametreler ekleyebilirsiniz.

Makalede Visual Studio Build-Event shell komutları, Macros üzerinden değişken kullanımı , Jenkins .net projelerindeki Build mantığı gibi konuları spesifik bir problem üzerinden açıklamaya çalıştım.

Umarım faydalı olmuştur.

 

]]>
https://bilisim.io/2020/02/27/visual-studio-post-build-script-hazirlanmasi-ve-jenkins-deployu/feed/ 0
Task Kullanımında Doğru Bakış Açısını Yakalamak https://bilisim.io/2019/10/23/task-kullaniminda-dogru-bakis-acisini-yakalamak/ https://bilisim.io/2019/10/23/task-kullaniminda-dogru-bakis-acisini-yakalamak/#comments Wed, 23 Oct 2019 08:12:00 +0000 https://bilisim.io/?p=6566 Bu makalemizde askenron işlemlerde Task yönetimi konusuna deyineceğim.Son zamanlarda sıklıkla karşılaştığım performans problemlerinin olduğu her yerde ilk akla gelen fikir iş parçacığını Task içerisine alınıp asenkron işletilmesi yönünde.  Doğru uygulandığında mantıklı olduğuna ben de katılıyorum fakat çoğu zaman sadece avantajlı yönden bakıp “Dar boğazı açalım” başlığında nitelendiriliyor. Peki sadece bu bakış açısı ile kod yazmak […]]]>

Bu makalemizde askenron işlemlerde Task yönetimi konusuna deyineceğim.Son zamanlarda sıklıkla karşılaştığım performans problemlerinin olduğu her yerde ilk akla gelen fikir iş parçacığını Task içerisine alınıp asenkron işletilmesi yönünde.  Doğru uygulandığında mantıklı olduğuna ben de katılıyorum fakat çoğu zaman sadece avantajlı yönden bakıp “Dar boğazı açalım” başlığında nitelendiriliyor. Peki sadece bu bakış açısı ile kod yazmak iş akışımızı etkiliyor olabilir mi ? Bu konuda bir yazı yazma gereksinimide tam olarak buradan çıktı.  Refactoring yaparken gördüğüm Task kullanımları sadece işi yapmak üzerine Task da yönetilen işin geri bildirimi, son durumu , hata yönetimi gibi konulara ne kadar yer veriliyor ?

Eskiden Thread yönetimini göze almak maliyetliydi. Bence bu maliyet beraberinde kontrollü, denetimi yüksek ve geri bildirimli kod akışları ortaya çıkarıyordu.

2010 yılında  .Net Framework 4.0 ile birlikte Task sınıfı tanıtıldı. Üstelik LINQ gücünü de kullanarak iş parçacıklarımızı farklı Task’lar üzerinde rahatça çalıştırabilir hale geldi. Peki işlerin bu denli kolaylaşması yazılımcılar nezlinde asenkron arkaplan kullanımında dezavantajlı durumların göz ardı edilmesine neden olmuş olabilir mi ?

Bu yazımızın Trick noktası Task kullanımına ihtiyaç duyulan kod bloklarında dikkat etmemiz gereken bakış açıları

 

  • Task’lar Asenkron olarak çalışır, bu nedenle işini ne zaman bitireceği belli değildir. Kod yazarken bu durum göz önüne alınmalıdır
  • Asenkron olarak işletilen Task akışı Main Thread akışındaki değerlere etki etmemelidir. Aksi durumlarda tutarsız veriler elde edilebilir.
  • Task içersinde yönetilen iş main thread üzerindeki kodu etkileyecekse ,Task işleminin sonucu Main Thread’de bekletilmelidir. Bu şekilde ana iş akışı task sonucuna göre doğru bir şekilde ilerleyebilir.
  • Task içerisinde yapılan işlem Main Thread’ı etkilemese de işlem sonucu önemli olabilir.
    Bu nedenle işlemin arkaplanda ki durumu veya sonucunun bilgilendirilmesi gerekebilir
  • Task içerisinde alınan hatalardan Main Thread sorumlu değildir, bu nedenle iş parçacığınızı doğru yönetmezseniz Main Thread akışınızın  iş parçacığında alınan hatadan haberi olmayacaktır.

 

]]>
https://bilisim.io/2019/10/23/task-kullaniminda-dogru-bakis-acisini-yakalamak/feed/ 1
AspNetCore 3.0 Swagger İmplementasyonu https://bilisim.io/2019/10/20/aspnetcore-3-0-swagger-implementasyonu/ https://bilisim.io/2019/10/20/aspnetcore-3-0-swagger-implementasyonu/#respond Sat, 19 Oct 2019 21:53:55 +0000 https://bilisim.io/?p=6568 NetCoreSwaggerImplementasyonuAspNet Core 3.0 üzerinde API ( dökümantasyon ve test süreçlerinin şirket içi birimlerde kullanımı  ve yönetilebilir olması açısından ) Swagger implementasyonunu inceleyeceğiz.]]> NetCoreSwaggerImplementasyonu

Merhaba ,

A :  Arkadaşlar API de değişiklik mi oldu ?
B : Evet , Bazı değişiklikler yaptık.
A : Madde üzerinde değişiklik yorumunu göremiyorum ?

*******************

A : Save Post Methoduna hangi parametreleri göndermem gerekiyor ,  dönüş yapar mısın ?
B : İlk fırsatta dönüş yapıyorum.. ( Toplantı , acil problemler ) 4 Saat Sonra..
B : Parametreleri gönderdim..

********************

A : API yanlış sonuç dönüyor kontrol eder misin ?
B : Hangi parametreleri gönderiyorusun ?
– 3 saat sonra
B : ?????

troll

Yukarıda gün içinde sıklıkla duyduğunuz cümleler varsa bir şeyler ters gidiyordur.
Ekipler aynı ofisde hatta yan yana bile çalışsa bu tarz senkron bozuklukları her zaman olabiliyor.
Özetlemek gerekirse bir problemin tanımına ulaşmak veya argümanlarıyla birlikte problemi tanımlamak teknik anlamda çözümlenmesinden çok daha fazla zaman alıyor..

Bu makalemizde API methotlarının dokümantasyon ve test süreçlerinin iyileştirilmesi adına AspNet Core 3.0 üzerinde API ( dökümantasyon ve test süreçlerinin yönetilebilir olması açısından ) Swagger implementasyonunu inceleyeceğiz.

 

Swagger Nedir ?

Basit anlamda API servislerimizin dökümantasyonunun çıkartılması ve kolaylıkla response alınmasını sağlayan bir araçtır. Microsoft’un API projelerinde kısmı dökümantasyon çıkaran Help sayfasının yanısıra API Methotlarının gösterim şekli , uygulanabilirliği  , test edilebilir olması ve dökümantasyon işini dinamik bir şekilde özetleyebilmesi açısından oldukça ön plana çıkmıştır.

Swagger API endpoint adreslemesini dinamik bir şekilde yapmaktadır. Aslında özünde json formatında API method bilgiler içermektedir.  Methodun alacağı parametreler , tipleri , default geri dönüş tipi , endpoint url bilgisi kısacası tüm schema bilgisini saklamaktadır.

Aşağıda kullanacağımız uygulamanın swagger.json schema bilgilerine ulaşabilirsiniz..
https://localhost:44380/swagger/v1/swagger.json

{
  "openapi": "3.0.1",
  "info": {
    "title": "WeatherForecastAPI",
    "version": "v1"
  },
  "paths": {
    "/api/WeatherForecast/Get": {
      "get": {
        "tags": [
          "WeatherForecast"
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/api/WeatherForecast/Save": {
      "post": {
        "tags": [
          "WeatherForecast"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WeatherForecast"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WeatherForecast"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WeatherForecast"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

 

Başlayalım

.NetCore 3.0 üzerinde Template API ile bir proje oluşturalım.
Projemize NugetPackageManager üzerinden Swashbuckle.AspNetCore Nuget’i yükleyelim.

SwaggerConfiguration

Sırasıyla Swagger Configürasyonu için gerekli kodları yazalım.

appsettings.json

Swagger için Route, Title ve EndPoint değerlerimizi config üzerinde parametrik olarak belirleyelim.

"SwaggerOpt": {
    "Route": "swagger/{documentName}/swagger.json",
    "Title": "API Swagger",
    "EndPoint": "v1/swagger.json"
  },
  .....

Startup.cs

Startup.cs sınıfı içerisinde ConfigureServices ve Configure methotlarında birkaç kural belirleyeceğiz.
Öncelikle appsetting.json üzerinde SwaggerOpt key ile tanımladığım parametreleri model üzerine Bind işlemini gerçekleştirelim.

SwaggerOpt ismiyle bir sınıf oluşturalım.

public class SwaggerOpt
    {
        public string Route { get; set; }
        public string Title { get; set; }
        public string EndPoint { get; set; }
    }

Configure methodu içerisinde;

GetSection ile config üzerinden swagger parametrelerini okuyacağız Bind ile swagger için oluşturduğumuz sınıfa aktaracağız.
Bind işlemi için swaggerOpt sınıfı değişkenleri appsetting.json üzerine tanımlanan parametrelerle aynı  olmalıdır.

SwaggerOpt _opt = new SwaggerOpt();
 Configuration.GetSection("SwaggerOpt").Bind(_opt);

Uygulamamızı swagger’dan haberdar etmemiz gerekiyor.  app.UseSwagger ve UseSwaggerUI bildirimlerini aşağıdaki gibi gerçekleştirebilirsiniz.

app.UseSwagger(op =>
            {
                op.RouteTemplate = _opt.Route;
            });

            app.UseSwaggerUI(op =>
            {
                op.SwaggerEndpoint(_opt.EndPoint, _opt.Title);
            });

ConfigureServices metodu içerisinde;

 

services.AddSwaggerGen(opt =>
            {
                opt.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
                {
                    Title = "WeatherForecastAPI",
                    Version = "v1"
                });
            });

 

Swagger için gerekli configurasyonlar tamamlandı, Şimdi Dummy Datayı generic bir yapıda implement edelim.

 

WeatherForecast Model

public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

        public string Summary { get; set; }
    }

 

Generic IDummy<T> Data interface

public interface IDummyData<T> where T : class
    {
        public IList<T> Data { get; set; }
    }

 

WeatherForecastData sınıfımız

public class WeatherForecastData : IDummyData<WeatherForecast>
    {
        public WeatherForecastData()
        {
            FillData();
        }

        private void FillData()
        {
            WeatherForecast = new List<WeatherForecast>
            {
                   new WeatherForecast { Date = new DateTime(2019, 10, 14), Summary = "Hot", TemperatureC = 26 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 15), Summary = "Hot", TemperatureC = 27 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 16), Summary = "Sweltering", TemperatureC = 32 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 17), Summary = "Cool", TemperatureC = 14 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 18), Summary = "Cool", TemperatureC = 10 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 19), Summary = "Balmy", TemperatureC = 20 },
                   new WeatherForecast { Date = new DateTime(2019, 10, 20), Summary = "Balmy", TemperatureC = 20 }
            };
        }

        private static IList<WeatherForecast> WeatherForecast { get; set; }

        public IList<WeatherForecast> Data { get => WeatherForecast; set => WeatherForecast = value; }
    }

 

Tekrar startup.cs ‘e dönüp ConfigureServices metodu üzerinde oluşturduğumuz WeatherForecastData sınıfımızı Singleton ayaklandıralım.

services.AddSingleton<IDummyData<WeatherForecast>, WeatherForecastData>();

Son adım olarak API Controller da methodlarımızı ekleyelim.
api/ControllerName/Method name şeklinde route belirledim.

api/WeatherForecast/Get
api/WeatherForecast/Post

[ApiController]
    [Route("api/[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly IDummyData<WeatherForecast> _weatherForecastData;

        public WeatherForecastController(IDummyData<WeatherForecast> weatherForcastData)
        {
            _weatherForecastData = weatherForcastData;
        }

        [HttpGet]
        [Route("Get")]
        public IActionResult Get()
        {
            return Ok(_weatherForecastData.Data);
        }

        [HttpPost("Save")]
        public IActionResult Add([FromBody] WeatherForecast forecast)
        {
            _weatherForecastData.Data.Add(forecast);
            return Ok(new { Success = true });
        }
    }

 

Startup.cs içerisine eklediğimiz kodların son halini bütün şekişlde burada görebilirsiniz.

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

     
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddSingleton<IDummyData<WeatherForecast>, WeatherForecastData>();

            services.AddSwaggerGen(opt => { opt.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo 
                                        { Title = "WeatherForecastAPI", Version = "v1" }); });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            SwaggerOpt _opt = new SwaggerOpt();
            Configuration.GetSection("SwaggerOpt").Bind(_opt);

            app.UseSwagger(op => { op.RouteTemplate = _opt.Route; });

            app.UseSwaggerUI(op => { op.SwaggerEndpoint(_opt.EndPoint, _opt.Title); });
        }
    }

 

Projeyi ayaklandırıp , localhost üzerinden swagger ‘ı tetikleyebilirsiniz..
https://localhost:44380/swagger/index.html

Artık projemizde API dökümantasyonumuz mevcut. Makalemizin başında yazdığımız problemlerin çözümün için Swagger !

 

Get

NetCoreSwaggerImplementasyonu

Post

 

 

]]>
https://bilisim.io/2019/10/20/aspnetcore-3-0-swagger-implementasyonu/feed/ 0
Linq OrderBy Trick https://bilisim.io/2019/10/14/linq-orderby/ https://bilisim.io/2019/10/14/linq-orderby/#respond Mon, 14 Oct 2019 06:51:47 +0000 https://bilisim.io/?p=5838 LinqToObject OrderBy]]>

Linq-to-SQL Linq-To-Object  hayatımıza girdiğinden beri kod yazmak daha bir zevkli hale geldi
İpucumuz da tam buradan geliyor OrderBy

OrderBy
Bildiğiniz gibi listeler içinde varsayılanı artan olacak şekilde artan (Asc) veya azalan (Desc) olarak sıralama yapmamızı sağlıyor veya nesne yardımıyla compare edilip sıralanabilir.

Standart kullanım

_productList.OrderBy(p=> p.Name); 
 // veya
_productList.OrderBy(p => p.Name).ThenBy(p => p.Category);

 

Peki kategorileriniz için spesifik bir sıralama yapmamız gerekseydi; Örneğin objemizdeki Category  üzerinde veri bazlı öncelik belirleme işlemi gibi…

Bizim senaryomuzda kategoriler için  “M” ve “C” değeri öncelikli olacak. Bu öncelik sonrasında ise Name özelliğini artan olarak sıralanacak.

Aşağıdaki kod bloğu ile OrderBy içerisinde bir sıralama mantığı belirledik.
Category “M” ise değeri 1 “C” ise 2  M ve C dışındakiler için 100 olarak ele alınacak. Daha sonra Name özelliğiyle ile artan sıralama yapılacak.

Listemizin sıralama dizilimi 1 , 2 , >2 (ikiden büyük olanlar) , A , Z  şeklinde ifade edilebilir..

_products.OrderBy(p => p.Category == "M" ? 1 : 
                       p.Category == "C" ? 2 : 100).ThenBy(p => p.Name);

1
1
2
A – Z

 

]]>
https://bilisim.io/2019/10/14/linq-orderby/feed/ 0
ASPNET .NetCore Api Response NamingStrategy Belirlenmesi https://bilisim.io/2019/10/10/aspnet-netcore-api-response-namingstrategy-belirlenmesi/ https://bilisim.io/2019/10/10/aspnet-netcore-api-response-namingstrategy-belirlenmesi/#respond Thu, 10 Oct 2019 06:45:09 +0000 https://bilisim.io/?p=6555 AspNet CORE Web API Response NamingStrategy belirlenmesi ve özel çevirimler]]>

Öncelikle Naming Conventions konusuna kısaca değinmek istiyorum.  Glabol düzeyde farklı insanların aynı veya birbirine yakın işleri yaptığı her platform için çeşitli standartlar belirlenmiştir. Yazılım dünyasında da kod standartları oldukça önemlidir, Microsoft teknolojileriyle yazılım geliştiriyorsanız ilk olarak Microsoft naming conventions  makalesini incelemenizde fayda var.

 

Bu makalemizde API üzerinden  response çıktılara global düzeyde müdahale edip çeşitli standartları veya kendi standartlarımızı uygulamayı örnekleyeceğiz.

Neye ihtiyacımız var ?

ASPNET .NetCore 3.0 üzerinden çalışan JSON API

Senaryo

Öğrenci bilgilerinin API response alınması ve kontrol edilmesi

Model ;

public class Student
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public string ClassRoom { get; set; }
    }

 

Başlayalım..

Öncelikle Startup.cs içerisinde bazı Configuration ayarlarını yapmamız gerekiyor. Aslında bahsettiğim configuration bir ContractResolver belirlenmesi. JSON Serialize işleminde kullanılacak resolver’ı API  tanıtalım.

.Net Core 3.0 öncesinde SerializerSettings için AddJsonOptions kullanılırken NetCore 3.0 ile birlikte AddNewtonsoftJson  methodunu kullanmamız gerekecek.
( Where did AddJsonOptions go in .NetCore 3.0 )

Nuget üzerinden  Microsoft.AspNetCore.Mvc.NewtonsoftJson paketini  yükleyelim veya .csproj dosyası içersinde aşağıdaki kodu eklememiz yeterli olacaktır.

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
</ItemGroup>

Startup.cs içersinde ConfigureServices methodunda JsonNet.Serialization lardan CamelCase’i set ediyoruz.  Defult olarak tanımlı NamingStrategy sınıflarına Newtonsoft.Json.Serialization altından erişebilirsiniz.

public void ConfigureServices(IServiceCollection services)
  {
            services.AddControllers()
                    .AddNewtonsoftJson(option => option.SerializerSettings.ContractResolver =
                                              new DefaultContractResolver
                                              {
                                                 NamingStrategy = new CamelCaseNamingStrategy()
                                              });
  }

Response üzerinde property isimlerninin camelCase kullanıldığını görebilirsiniz.

[
   {
      "id":1,
      "firstName":"Ali",
      "lastName":"Nazik",
      "birthDate":"1990-05-15T00:00:00",
      "classRoom":"Teknik1"
   },
   {
      "id":1,
      "firstName":"Veli",
      "lastName":"Gümüş",
      "birthDate":"1989-05-01T00:00:00",
      "classRoom":"Teknik2"
   },
   {
      "id":1,
      "firstName":"Mehmet",
      "lastName":"Bakır",
      "birthDate":"2000-08-25T00:00:00",
      "classRoom":"Teknik3"
   }
]

Custom NamingStrategy

Bazı durumlarda isimlendirme standartlarını kendiniz belirlemek istersiniz veya stabil bir standartlara zorlanabiliriz. Bu tarz durumlar için bazı özelleştirmeler yapılmalı.

Örneklendirme için   _{PropName}_   şeklinde bir standartlandırmaya zorlandığımızı düşünelim.

Standart isimlendirme sınıflarını kontrol ettiğimizde (örn. CamelCaseNamingStrategy) NamingStrategy abstract sınıfı base aldığını görüyoruz.  Abstract methodunu override edip base methodu eziyoruz

 

public class MyCustomNameStrategy : NamingStrategy
    {
        protected override string ResolvePropertyName(string name)
        {
            return $"_{name}_";
        }
    }

Son olarak Startup.cs içerisindeki ConfigurationSerrvices methodumuzda set edilen ContractResolver’ın NamingStrategy özelliğini set edelim.

 

NamingStrategy = new MyCustomNameStrategy()

 

Output

[
   {
      "_Id_":1,
      "_FirstName_":"Ali",
      "_LastName_":"Nazik",
      "_BirthDate_":"1990-05-15T00:00:00",
      "_ClassRoom_":"Teknik1"
   },
   {
      "_Id_":1,
      "_FirstName_":"Veli",
      "_LastName_":"Gümüş",
      "_BirthDate_":"1989-05-01T00:00:00",
      "_ClassRoom_":"Teknik2"
   },
   {
      "_Id_":1,
      "_FirstName_":"Mehmet",
      "_LastName_":"Bakır",
      "_BirthDate_":"2000-08-25T00:00:00",
      "_ClassRoom_":"Teknik3"
   }
]

 

 

]]>
https://bilisim.io/2019/10/10/aspnet-netcore-api-response-namingstrategy-belirlenmesi/feed/ 0
Son Başarılı Kodu Çalıştırmak İster misiniz? (Do You Want to Run the Last Successful Build) https://bilisim.io/2019/05/09/son-basarili-kodu-calistirmak-ister-misiniz-do-you-want-to-run-the-last-successful-build/ https://bilisim.io/2019/05/09/son-basarili-kodu-calistirmak-ister-misiniz-do-you-want-to-run-the-last-successful-build/#respond Thu, 09 May 2019 14:53:33 +0000 https://bilisim.io/?p=6208 Hepimiz kodumuzu yazdıktan sonra bunu debug etmek için F5 tuşuna basarız.  Visual Studio derleme hatası alır ve bize o ünlü soruyu sorar. Son başarılı yapıya devam etmek ve çalıştırmak ister misiniz?(Do You Want to Run the Last Successful Build). Ömrüm boyunca bu şekilde kodu çalıştırmak istemedim. Nedeni de sanırsam hatalı çalışan şeylerden hoşlanmamam olabilir. Kim […]]]>

Hepimiz kodumuzu yazdıktan sonra bunu debug etmek için F5 tuşuna basarız.  Visual Studio derleme hatası alır ve bize o ünlü soruyu sorar. Son başarılı yapıya devam etmek ve çalıştırmak ister misiniz?(Do You Want to Run the Last Successful Build).

Ömrüm boyunca bu şekilde kodu çalıştırmak istemedim. Nedeni de sanırsam hatalı çalışan şeylerden hoşlanmamam olabilir. Kim niye çalıştırmak ister derseniz. Web uygulamalarında hatalı bir sayfa veya bir geliştirici belli bir yerde işini bitirmemiş olması gibi durumlarda  farklı bir projedeki servisleri yenilemeniz için kullanılabilir.  Ayrıca bu mesajdan kurtulmak istiyorsanız. İşte size yöntemi.

Tools | Options | Projects and Solutions | Build and Run.

“On Run, when build or deployment errors occur” içindeki değeri “Do Not Launch” yapmanız yeterlidir.

Kaynakça

Peter Vogel

]]>
https://bilisim.io/2019/05/09/son-basarili-kodu-calistirmak-ister-misiniz-do-you-want-to-run-the-last-successful-build/feed/ 0