.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.
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
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 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.

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üklemesudo apt-get update sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install dotnet-sdk-3.1
sudo apt-get update sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install aspnetcore-runtime-3.1
sudo apt-get update sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install dotnet-runtime-3.1
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.
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.
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.

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.
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.
Ö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.

( 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 ;
Ü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 ]
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 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
]]>
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 :)]]>
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”
İnsanlar kısa süreli bellekte sadece 7 maddeyi tutabilirler.Temiz kod yazarken bu kuralı unutmayalım.
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);
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.
if()
{
if()
{
if()
{
}
}
}
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.
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 ;
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
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()
}
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.
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);
}
]]>
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 :)]]>
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.

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)
![]()
if(done)
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 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;
}
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;

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)
İ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)
![]()
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)
{
}
İ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
]]>
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 Events > Post-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.

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.

Ş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.
]]>
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ı
]]>
AspNet 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.]]>
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 : ?????

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.
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
}
}
}
}
.NetCore 3.0 üzerinde Template API ile bir proje oluşturalım.
Projemize NugetPackageManager üzerinden Swashbuckle.AspNetCore Nuget’i yükleyelim.
Sırasıyla Swagger Configürasyonu için gerekli kodları yazalım.
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 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; }
}
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);
});
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

Post

]]>
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

]]>
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.
ASPNET .NetCore 3.0 üzerinden çalışan JSON API
Öğ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; }
}
Ö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"
}
]
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"
}
]
]]>
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
]]>