Erhan Ballıeker

Asp.Net Core 2.0’a Giriş – Bölüm 2 (Program.cs, WebHostBuilder, Kestrel hakkında)

Selamlar Arkadaşlar, bu yazımda geçen yazıda başladığımız AspNet Core Web Application oluşturma ve solution structer ına bir göz gezdirmeden sonra, Program.cs e ve içeriğindeki metodlara değineceğiz. Sonrasında sırası ile, Startup.cs dosyasına, bakıp AspNetCore Dependency Injection, Middleware yapısı vb konulara değinerek bu seriyi tamamlayacağız.

Öncelikle klasik AspNet ten hatırladığımız System.Web assembly sinden ve ne yaptığından bahsedip Core tarafına o şekilde geçelim.

Klasik Asp.Net projelerinde, bir web projesinin ayağa kalkma süreci System.Web assembly sindeki kodlar tarafından sağlanırdı ve sizin buna müdahele şansınız sadece size Global.asax dosyasında müdahele etmeniz, konfigürasyonlar yapmanız için verilen bazı metodları editlemekle kısıtlıydı. Artık yeni AspNet Core tarafında, application ın tümüyle başlatılma kısmı sizin elinizde. Nasıl olduğuna biraz daha derinlemesine değinelim.

Asp.Net Core projelerinde solution içerisinde bir adet Program.cs dosyası mevcut.  Bu dosya içerisindeki Main() metodu artık sizin application nınızın entry point i. Uygulumanızı başlattığınızda, Runtime uygulama içerisinde ki bu metodu arar ve çalıştırır. Şu aşamada Asp.Net Core Web App projeniz aslında bir Command Line Application olarak yaşamına başlar.

public class Program
{
     public static void Main(string[] args)
     {
         CreateWebHostBuilder(args).Build().Run();
     }

     public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
         .UseStartup();
}

Uygulama çalıştığında Main() metodunuz, CreateWebHostBuilder Metodunu çağırır ve geri dönen IWebHostBuilder interface i üzerindeki Build() ve Run() metodlarını tetikler. Bu IWebHostBuilder metodu üzerindeki Run() metodu tetiklendiği andan itibaren artık uygulamanız gerçekten bir Asp.Net Core Web Application a dönüşür 🙂

Asp.Net Core un open source olmasından faydalanalım ve bu CreateDefaultBuilder(args) metodu arka planda ne yapıyor bir bakalım.

Buradan gördüğümüz gibi CreateDefaultBuilder(args) metodu aşağıdaki işleri yapıyor. Tek tek ne olduklarına değinmeye çalışalım.

public static IWebHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new WebHostBuilder();

            if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey)))
            {
                builder.UseContentRoot(Directory.GetCurrentDirectory());
            }
            if (args != null)
            {
                builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
            }

            builder.UseKestrel((builderContext, options) =>
                {
                    options.Configure(builderContext.Configuration.GetSection("Kestrel"));
                })
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var env = hostingContext.HostingEnvironment;

                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

                    if (env.IsDevelopment())
                    {
                        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                        if (appAssembly != null)
                        {
                            config.AddUserSecrets(appAssembly, optional: true);
                        }
                    }

                    config.AddEnvironmentVariables();

                    if (args != null)
                    {
                        config.AddCommandLine(args);
                    }
                })
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddConsole();
                    logging.AddDebug();
                    logging.AddEventSourceLogger();
                })
                .ConfigureServices((hostingContext, services) =>
                {
                    // Fallback
                    services.PostConfigure(options =>
                    {
                        if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
                        {
                            // "AllowedHosts": "localhost;127.0.0.1;[::1]"
                            var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                            // Fall back to "*" to disable.
                            options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
                        }
                    });
                    // Change notification
                    services.AddSingleton(
                        new ConfigurationChangeTokenSource(hostingContext.Configuration));

                    services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
                })
                .UseIIS()
                .UseIISIntegration()
                .UseDefaultServiceProvider((context, options) =>
                {
                    options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
                });

            return builder;
        }

Burada aslına bir takım kontroller ve konfigürasyonların dışında değinmek istediğim yerler şimdilik şunlar ;

  • builder.UseContentRoot
  • builder.UseConfiguration
  • builder.UseKestrel
  • .UseIIS()
  • .UseIISIntegration()
  • .ConfigureServices((hostingContext, services)
  • .UseDefaultServiceProvider((context, options)

bu metodlar ile initialize edilen WebHostBuilder için aşağıdaki ayarlar default olarak yapılıyor.

  • builder.UseContentRoot(Directory.GetCurrentDirectory());
    • Burada projenin content root konfigürasyonu yapılıyor. Project Directory si Content Root olarak set ediliyor.
  •  builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
    • Burada projenin settings konfigürasyonu yapılıyor. Default olarak appsettings.json dosyasında konfigürasyonlar okunup initiate ediliyor.
  • builder.UseKestrel((builderContext, options) => { … }
    • Kestrel AspNet Core için yazılmış olan internal bir Web Server. Tabii ki bir IIS, Nginx, Apache gibi donanımlı ve yetenekli değil, ama oldukça lightweight ve performanslı çalışan bir web server. Bunun detaylarına daha sonra gireceğiz ama özetle burada Asp.Net Core Web App projesinin internal olarak Kestrel i kullanması söyleniyor ve Kestrel in bazı konfigürasyonları yapılıyor.
  • .UseDefaultServiceProvider((context, options)
    • Burada WebHostBuilder a söylenen Asp.Net Core projesinin internal Dependency injection container ını kullanması. Dependency injection artık Core ile birlikte built-in gelen bir support. Object Creation lar, ve onların lifetime managementları için bunu doğrudan kullanabiliriz. Yada kendi istediğimiz IoC Containerları kullanmasını söyleyebiliriz. Burada Default olarak built-in gelen container ın kullanılması söyleniyor.
  • .ConfigureServices((hostingContext, services)
    • Burada, Builder a kullanması için verilen IoC Container a, Proje ile alakalı bazı type ların register edilmesi sağlanıyor.
    • örneğin;  services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); Transient (obje container dan her istendiğine yenisi oluşturulup veriliyor) olarak IStartupFilter istendiğinde, HostFilteringStartupFilter ın proje içerisinde kullanılması ayarları yapılıyor.
  • .UseIIS()
  • .UseIISIntegration()
    • Burada, Web uygulamasının external web server olarak IIS kullanması söyleniyor. Gelen  requestler önce IIS den geçip, internal olan Kestrel e ulaşacak, sonrasında da middleware lerin eklenme sırasına göre her bir middleware de ilgili process ler gerçekleşip, aynı şekilde önce Kestrel e sonra da IIS e response iletilecek, ve response client tarafına dönecek.

 

Bu yazımıza da burada son verelim. Bir uygulamanın hayatına bir command line application olarak başlayıp, nasıl bir Asp.Net Core Web App e dönüştüğünü yüzeysel olarak source kodları inceleyerek anlamaya çalıştık. Bir sonraki yazımızda da Startup.cs dosyasını inceleyip artık bir Asp.Net Core Web Application ın, nasıl oluştuğunu, solution un neler içerdiğini ve konfigürasyonlarının yapılıp hayatına nasıl başladığını tamamen anlamış olacağız.

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

NetStandard nedir? .NetCore, .NetFramework ve diğer .Net Platformları ile ilişkisi nasıl? (Bölüm 3)

Selamlar, .NetStandard ile ilgili örnek yapıp bir de developer gözünden bir metafor ile netstandard a bakıp bu seriyi sonlandıracağım yazıma hoşgeldiniz. Önceki yazıları bundan önce okumanızda fayda var. Bölüm1 ve Bölüm2 den sonra buraya devam ederseniz daha tatmin edici olacaktır sizin için 🙂

Önce şu bahsettiğim ve aslında şurada yazılmış olan ve çok hoşuma giden matefordan bahsedelim 🙂 Bir developer için .NetStandard ın, tüm .NetPlatformlarının implemente etmesi gereken API lar bütünü olduğundan bahsetmiştik. Yan, bu durumda her bir .NetStandard versiyonunu bir interface, her bir .Net Platformunu da bir concerete class olarak düşünmek yanlış olmaz 🙂

Bu durumda da aslında şöyle bir .cs dosyası ile tüm NetStandard versiyonlarını ve onu implemente eden platformları yazabiliriz.

Önce .NetStandard ı görelim;

// .NET Standard

interface INetStandard10
{
    void Primitives();
    void Reflection();
    void Tasks();
    void Collections();
    void Linq();
}

interface INetStandard11 : INetStandard10
{
    void ConcurrentCollections();
    void EventSource();
    void InteropServices();
}

interface INetStandard12 : INetStandard11
{
    void ThreadingTimer();
}

interface INetStandard13 : INetStandard12
{
    void AppContext();
    void Console();
    void StringInterpolation();
    void Calendars();
    void FileSystem();
    void Sockets();
    void AsyncLocal();
}

interface INetStandard14 : INetStandard13
{
    void CryptoECDsa();
}

interface INetStandard15 : INetStandard14
{
    void EventCounter();
}

interface INetStandard16 : INetStandard15
{
    void LinqInterpretation();
    void CryptoECCurve();
    void PrecompiledRegex();
}

interface INetStandard20 : INetStandard16
{
    void EverythingThatIsInNetFrameworkAndXamarin();
}

yukarıda gördüğünüz ve daha önce söylediğimiz gibi, .NetStandard versiyonları PCL den farkli olarak lineer bir şekilde gidiyor. Her bir versiyonda da, platformların implemente etmesi gerektiğini düşündükleri (required olarak label ladıkları API ları görüyorsunuz)

Peki birde .NetFramework ve .NetCore implementasyonlarına bakalım;

// .NET Framework

class NetFramework45 : INetStandard11
{
    // ...
}

class NetFramework451 : NetFramework45, INetStandard12
{
    // ...
}

class NetFramework452 : NetFramework451
{
    // ...
}

class NetFramework46 : NetFramework452, INetStandard13
{
    // ...
}

class NetFramework461 : NetFramework46, INetStandard20
{
    // ...
}

class NetFramework462 : NetFramework461
{
    // ...
}

class NetFramework47 : NetFramework462
{
    // ...
}

class NetFramework471 : NetFramework47
{
    // ...
}

// .NET Core

class NetCore10 : INetStandard16
{
    // ...
}

class NetCore11 : NetCore10
{
    // ...
}

class NetCore20 : NetCore11, INetStandard20
{
    // ...
}

Burada da, kolayca .NetStandard 2.0 ın Framework 4.6.1 tarafından tamamen implemente edildiğini, aynı şekilde .NetCore1.0 ın NetStandard 1.6 yı implemente ettiğini,  .NetCore 2.0 ın da .NetStandard 2.0 ı tamamen implemente ettiğini görüyorsunuz:)

Bir yazılımcı için anlaşılması çok daha kolay oldu değil mi 🙂

Bir de .NetCore CLI kullanarak küçük bir örnek yapalım, Visual Studio 2017 den sonra zaten bunu IDE üzerinden yapmayı göstermenin bir lüzumu yok 🙂 . Hem Cross Platform Command Line Interface i biraz kullanmış tanımış oluruz, hemde hakkında bu kadar yazdığımız .NetStandard ı bir de kod tarafından görmüş oluruz.

Öncelikle Windows tarafında Command Prompt u, Mac tarafında da ise Terminal i açıyoruz. Windows tarafı ile Mac tarafında hiçbir kod farklı olmadığı, yazanları iki tarafta da rahatça deneyebilirsiniz.

İlk olarak aşağıdaki kodu çalıştırarak yeni bir class library projesi oluşturuyoruz. bu proje otomatik olarak .NetStandard olarak oluşacaktır.

$ dotnet new classlib -o mystandardlibrary

bu şekilde bir standard class library projesi oluşturmuş olduk. Şimdi Class1 in içeriğini değiştirip basit bir static metod koyalım.

using System;

namespace mystandardlibrary
{
    public class Class1
    {
        public static string GetMessage() => "Hello from .NET Standard!";
    }
}

Şimdi aşağıdaki komut satırını çalıştırarak yeni bir aspnet core projesi oluşturalım. web keyword ü ile default bir aspnet core projesi oluşacak.

$ dotnet new web -o aspnetcoreclient

Oluşturmuş olduğumuz web projesine, class library projesini referans olarak verelim.

$ dotnet add reference ../mystandardlibrary/mystandardlibrary.csproj

Şimdi de son olarak, web projesindeki Startup.cs dosyasında bulunan inline yazılmış ve gelen request e direk cevap veren tek middleware kodunu edit leyip, aşağıdaki gibi, standard class librarydeki metodumuzu kullanacak şekilde düzenleyelim.

app.Run(async (context) =>
{
    var message = mystandardlibrary.Class1.GetMessage();
    await context.Response.WriteAsync(message);
});

Son olarak, web projesi içerisindeyken önce “dotnet build”  projemizi derleyip, sonrada aşağıdaki komut satırını çalıştırarak web projesini ayağa kaldıralım.

$ dotnet run

core

resimde görmüş olduğunuz gibi http ve https portları üzerinde sitemiz 5000 ve 5001 portlarında ayağa kalktı. Browser ımızı açıp http://localhost:5000 adresine gidersek aşağıdaki gibi, web projemizin, NetStandard kütüphanemizdeki kodu çalıştırdığını göreceğiz. 

localhst

Birkaç satır kod ile, tüm .Net Platformlarında kullanabileğimiz .NetStandard2.0 kütüphanemizi oluştumuş ve bir aspnet core projesinde kullanmış olduk. 🙂 Bu 3 yazılık seri de, Genel olarak kod paylaşımını, NetStandard ı ve onun diğer .Net platformlar ile olan ilişkisini anlatmaya çalıştım.Umarım keyif almışsınızdır.

.Net Core ile ilgili detaylı yazı serilerinde görüşmek üzere,

NetStandard nedir? .NetCore, .NetFramework ve diğer .Net Platformları ile ilişkisi nasıl? (Bölüm 2)

Selamlar, bir önceki yazımda anlatmaya başladığım .NetStandard hakkında bilgiler vermeye devam edeceğim bu yazımdan önce, eğer okumadıysanız 1.Bölüm‘ü okumanızda fayda var.

Önceki yazıyı, buradaki kaynaktan aldığım resimle bir özetleyelim 🙂

standarddiagram

Resimde de görüldüğü gibi .NetStandard, birbirinden farklı .Net platformlarında ortak kod paylaşımı yazmayı kolaylaştırmak için tüm platformların uyması gereken bir standard getirerek .Net Platformlarını birleştirmek ve ileride farklı platformların da doğmasıyla çıkabilecek başka sorunlara da çözüm olmak amacı ile ortaya çıktı.

Bu yazımıza biraz daha soru cevap şeklinde devam edeceğim. Farklı sorularınız olursa yorum yazmaktan çekinmeyiniz.

Kütüphane yazarken hangi .NetStandard versiyonunu seçmeliyim?

Aslında bu sorunun cevabı çok net. .NetStandard yerine geçtiği PCL aksine lineer olarak versiyonlanıyor. Yani daha yüksek versiyon demek daha fazla ortak API kullanımı, ve eski .NetStandard versiyonlarına destek demek. Bu yüzden yeni oluşturacağınız Class Library leriniz için bügünden itibaren .NetStandard 2.0 seçmeniz en mantıklısı olacaktır.

Bununla beraber şunu da söylemekte fayda var, .NetStandard versiyonları immutable olarak sunuluyor. Yani bir versiyon release olduğunda ona artık dokunulmuyor. Yeni bir versiyonda olması planlanan API lar önce platform spesifik ortamlarda implemente ediliyor (örneğin .NET Core). Eğer ekip bu API ın tüm platformarda olması gerektiğine karar verirse yeni .NetStandard versiyonuna bu API ekleniyor. Yani geçmişle uyumsuzluk yok, ve hangi API ların .NetStandard a ekleneceği çok dikkatlice seçiliyor.

Peki bu .NetStandard da neler olacağına kimler karar veriyor?

Hemen akla gelen ilk isim doğru tabii, Microsoft 🙂  Önemli .Net Plarformlarının(NET Framework, .NET Core, ve Mono) implemente eden firma olarak Microsoft başta olmak üzere, Unity asıl rol oynuyır diyebiliriz. Eğer isterseniz sizlerde buradan bu sürece katılabilir, fikirlerinizi söyleyebilirsiniz. Belki önereceğiniz bir API bir sonraki versiyonda hayatımıza girecektir =)

.NetStandard 2.0 ile gelen .NET Framework Uyumluluk modu nedir?

Bu uyumluluk modu sayesinde bir .NetStandard class library projenize, .NetFramework library sini, sanki .NetStandard 2.0 için derlenmiş gibi ekleyebilirsiniz. Fakat tahmin edeceğiniz üzere her API çalışmayacaktır. Örneğin, WPF için yazılmış bir API ı, .NetStandard projenize elediğinizde, onu Xamarin vs için kullanamayacaksınız. Peki bu şekilde çalışması kesin olmayan bir API eklediğinizi nasıl anlayacaksınız?

rest

bu itici ünlem işareti ve uyarısı tanıdık geldi mi? 🙂 İşte bu uyarı Visual Studio size şunu söylüyor;

  • Ben istediğin paketi, sanki NetStandard için derlenmiş gibi projeye eklemeye çalıştım fakat, bu pkaetteki her API buna uyumlu değil. Bu yüzden bunu en uyumlu kullanabileceğin versiyon ile restore ettim. Eğer sorun yaşamazsan ne ala, yaşarsan günah benden gitti 🙂

diyor. Eğer gerçekten sorun yaşamaz, eklediğiniz API ın düzgün çalıştığını görürseniz. bu mesajı her derlemede görmemek için şunu yapabilirsiniz. Proje dosyasını açarak aşağıdaki gibi ilgili paketin ve versiyonunun adını yazarak bu uyarıdan kurtulabilirsiniz.

<ItemGroup> <PackageReference Include="XXX" Version="XXX" NoWarn="NU1701" /> </ItemGroup>

 Ben tam olarak neden PCL yerine .NetStandard kullanayım anlamadım

Diyecek olanınız var ise şöyle diyeyim;

Evet ikisininde nihai amacı aynı, ortak binary kod paylaşımı sağlamak. Ama arada ciddi farkları var.

  1. .NetStandard a eklenecek API lar özenle seçilip, önce platform spesifik taraflarda implemente edilip sonrada tüm platformlarda olup lmadığına karar veriliyor. PCL ise sadece farklı platformlar için kullanılan BCL lerdeki ortak API ları alıyor. Bir nevi sadece kesişim kümesini alıp çekiliyor aradan.
  2. .NetStandard da emin olabilirsiniz ki, yeni bir versiyon ile geride kalan tüm versiyonları desteklersiniz. Lineer bir versiyonlama vardır. Ama PCL bu şekilde çalışmıyor.
  3. Belkide en önemli fark şu ki; PCL Microsoft bağımlı iken, .NetStandard platform-agnostic. Hatta bir adım ötesi, Microsoft a özel olmamakla beraber .NetStandard aynı zamanda C# bağımsız. Herhangi bir language-spesific api bulunmuyor ve eklenmesi de düşünülmüyr henüz. Aslında zaten doğasında bu var, böyle olmak zorunda çünkü artık .Net platformları hemen hemen her yerde çalışıyor =)

.NetStandard ile destekleyebileceğim maksimum .Net Framework versiyonu nedir?

Şu an için 2.0 ile beraber Framework 4.6.1 i full kullanabiliyor olucaksınız.

.NetStandard a eklenecek API lara nasıl karar veriliyor?

Bunun için öncelikle Hem .NetFramework tarafında hem de Xamarin tarafında çalışan API lar göz önünde bulunduruluyor. Bu aşamadan sonra API ları required veya optional  ikiye ayırıyorlar. Required olanlar tamamen implemente edilmeye çalışılırken optional olanlar ayrı nuget paketleri halinde .NetStandard için uyumlu olucak şekilde derlenmeye çalışılıyor. Fakat nuget paketi olarak gelen her API ın her fonksiyonunun çalışma garantisi yok daha önce belirttiğim gibi.

Peki Platform spesifik API ların ne olacağına nasıl karar veriyorlar?

Tüm platformlarda çalışacak API lar yazmaya çalışılırlen karşılaşılan en büyük sorunlardan biri de, Platform spesifik API lar ile ilgili nasıl ilerleneceği. Bu tür API lara örnek olarak;

  • Runtime Spesific API lar
  • Operating System Spesific  API lar

Runtime spesifikler için reflection ile, runtime da kod oluşturup çalıştırmayı düşünebiliriz. Ama iOS buna izin vermiyor mesela, yada JIT compiler a sahip olmayan Windows platformları da bunu yapamayacak. Windows Registry de OS Spesifik olarak düşünebiliriz.

Peki siz olsanız bu durumda nasıl karar verirdiniz? Olasılıkları düşünelim

  1. İlgili API ı direk Unavailable yapmak
  2. .NetStandard a yine de eklemek ama desteklenmeyen platformlarda hata fırlatmak
  3. API simüle etmek 🙂

3. maddeye örnek olarak şunu söylebiliriz. Mono Registry API implementasyonu .ini dosyaları üzerinden yapıyor. Bu sayede uygulama kendi state i ile ilgili bilgileri registry de saklayabiliyor, fakat kendi dışında, mesela işletim sistemi ile ilgili bilgileri oradan okuyamıyor.

.NetStandard ekibi de sizin gibi düşünmüş olacak ki, hepsinin karışımı bir yol izlemek istiyorlar 🙂 Öncelikle .NetStandard’a sadece her platformda çalışacak API ları ekliyorlar, sonrasında popüler olanları ayrı nuget paketleri halinde .NetStandard ın üzerinde oturacak şekilde nuget e koyuyorlar(Tekrar belirtelim her fonksiyon çalışmayabilir). Bu kendi halinde ayrıştırılabilen ve kendi kendine yeten paketler için güzel çalışan bir yöntem. Fakat API ın tamamı bu şekilde değilse, 2. yada 3. yolu izliyorlar.

Peki gelelim esas sorulardan birine 🙂

.NetCore ile .NetStandard arasındaki bağ nedir?

.NetCore diğer .Net Platformlarına kıyasla en az API sayısına sahip ve en yavaş, dikkatli geliştirilmesi gereken bir framework. Sebebi belli, NetStandard ın tüm .Net Platformalarında çalışması istendiği gibi, .Net Core un da, neredeyse tüm OS, ve mimari seviyelerde çalışabilmesi isteniyor. Ortaya çıkış amacı bu zaten. Windows/IIS vs. bağımlılığın ortadan kalktığı her ortamda çalışabilen bir ürün olması. Ama runtime, OS level vb farklılıklar için ortak kod yazmak .NetStandard ın geliştirilmesi gibi hızlı gitmiyor. .NetStandard ın her bir versiyonunun immutable olduğundan söz etmiştik. Ve nasıl API ların seçildiği. Her bir platform kendine özgü API ları eklerken bir yandan da hangilerinin universal olabileceklerine karar veriyorlardı.

En nihayetinde durum .NetCore ve .NetStandard ı ayırmakla son buldu, çünkü tüm .net Platformlarında çalışacak olan API ları .NetCore tarafına ekleyip tüm işletim sistemi ve mimarilerde çalıştırmak mümkün olmuyordu.

Bu yüzden de .NetCore tarafından diğer platformlarda bulunan uyumluluk modunu kaldırdılar.

Artık NetCore bağımsız bir şekilde daha hızlı ilerliyor, ve yine gün sonunda core dahil olmak üzere ortak olması gerektiğini düşündükleri API ları, yeni .NetStandard versiyonlarına ekliyorlar.

.NetCore 2.0 tümüyle .NetStandard 2.0 uyumlu olduğundan, korkacak birşey aslında yok 🙂 Ama bu noktaya gelene  kadar ki sürece bilmek te fayda olduğunuz düşünüyorum

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