Erhan Ballıeker

Asp.Net Core Dışında Bir .NetStandard Kütüphanemizde (Ör: Xamarin.Forms Shared Projede) HttpClientFactory Kullanımı.

Selamlar,

Başlık konusunda biraz kararsız kalmıştım. Değinmek istediğim noktayı şöyle anlatayım. Asp.Net Core projesi dışında bir Client ınız olduğunu düşünelim (Console App, Xamarin Mobile App, WinForms, WPF, UWP vs vs..), bu projeden API call ları HttpClientFactory kullanarak nasıl yapacaksınız buna değinmek istiyorum.

Önceki yazılarımda Networking taraflarındaki konulara çokça değinmişimdir. Günümüzde de bir .net projesinde http üzerinden haberleşmek için artık elimizde en iyi sınıf HttpClientFactory gibi duruyor. Ama danışmanlıklarım sırasında şunu çok gördüm.

Asp.Net Core un genel olarak çalışma yapısı insanlarda oturmamış. Microsoft un tüm paketleri ayrı ayrı yapmasının sebebi çok anlaşılmamış. .NetStandard nedir, Core nedir, kafalarda karışıklıklar var.

Bunlar la ilgili konulara değindiğim yazılarıma bakmanızı tavsiye ederim.

İnernettki örneklerin birçoğu HttpClientFactory kullanımını bir Asp.Net Core projesi üzerinden anlatıyor. Asp.Net Core projesinde ki startup dosyasında IServiceCollection a httpclientfacory yi, named typed vs gibi farklı tiplerde istediğiniz gibi tanıtıp sonra da kullanıyorsunuz.

Ama bir xamarin projesi karşımıza çıktığında ve bu xamarin projesinden dışarıya yapacak olduğumuz requestleri HttpClient ile değilde HttpClientFactory ile olmasını istetdiklerinde, ortalıka ne bir Startup var, ne ServiceCollection vs vs.

Bu durumda yapıpyı kurmakta zorluk çekildiğini gördüm.

Önce şundan bahsedelim.

Asp.Net Core daki IServiceCollection ın Asp.Net Core projesine geldiği dll

Microsoft.Extensions.DependencyInjection paketi. Peki bu paketi ben başka bir .NetStandard kütüphaneme ekleyemez miyim? Evet ekleyebilirim.

.NetStandard ın ne olduğunu hatırlayalım kısaca;

  • Microsoft un .netframework ünün birden fazla implementasyonu var. Yani bu framework ü bir Interface olarak düşünürseniz farklı implementasyonlardan kastım şunlar olucaktır;
    • UWP
    • WPF
    • Asp.Net – Asp.Net Core
    • Xamarin
    • Mono
    • vs..
  • Tüm bu platformlar arasında kod paylaşımı için .netstandard a kadar iki farklı yönetmimiz var idi. Portable Class Library ler ve Shared Asset Project idi.
  • Microsoft artık tüm platformlar arasında kod paylaşımını kolaylaştırmak için ortaya bir standard koydu. .NetStandard. Artık her implementasyon bu standard içerisindeki API ları kendine eklemek zorunda idi
  • İlk başlarda çok fazla API içerisinde barındırmayan .NetStandard artık 2.0 versiyonu ile veraber tüm full .netframework 4.6.1 deki api lara bile sahip hale geldi. İçerisinde artık binlerce API var. Bu da şu demek, eğer nugetten indirdiğiniz bir kütüphane ya da sizin target olarak .NetStandard seçerek oluşturmuş olduğunuz bir kütüphane neredeyse üm .net implementasyonlarında çalışır olucak demek.

Peki bu özet bilgi ile şöyle bir işe kalkışalım. Bir solution ımız olsun. Bu solution içerisinde şunlar olsun.

  • Bir Asp.Net Core ile yazılmış API
  • Bir Asp.Net Core Web App (Client)
  • Bir  Xamarin iOS ve Android App (Client)
  • Bir Console App (Client)
  • Bir WPF App (Client)

Ve şunu istiyor olalım.

Tüm clienlar çoğunlukla olacağı gibi aynı Apı mız ile haberleşerek istediği işlemleri yapsın. Ve bu Api ile haberleşirken de HttpClient değil HttpClientFactory kullansın.

Hatta Eski yazılarımda olduğu gibi öyle bir Api Haberleşme sınıfı yazalım ki, buraya Refit, Polly gibi kütüphaneleri de kullanıp yine güzel bir Resilient Network Service altyapısı oluşturalım.

Tüm bunlaru yapmadan önce başlıkta dediğim gibi bir xamarin uygulamasında HttpClientFactory yi nasıl kullanırım bunu görelim.

Bunu yapmanın aslında ne kadar basit olduğunu görünce biraz şaşırabilirsiniz özellikle .Netstandard ve .Net Core ile beraber Microsoft un birçok paketi ayrı ayrı sunmasının faydalarını daha önce keşfetmediyseniz…

Bir xamarin uygulamasını neredeyse bir Asp.Net Core uygulaması gibi görünmesini sağlamak için yapmam gereken ilk şey 2 adet paketi .NetStandard kütüphaneme eklemek. Platforms spesifik projelerle şuan için işim yok. Boş bir xamarin.forms projesi açtıktan sonra (Empty template ile) ilk olarak aşağıdaki paketleri yüklüyorum nugetten.

bu iki paket şunlar;

  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Http

ilk paket ile uygulamama bir DI özelliği katıyorum, bunun içinde autofac, unity, ninject vs gibi bir IoC container kullanmadan Microsft un kendi extension paketi üzerinden yürüyorum. Bu noktada artık bir Asp.Net Core projesinde yaptığımız gibi her istediğimiz api ı bu container a ekleyip bunun üzerinden erişbilir olduk bile.

Diğer pakete de bana Asp.Net Core projesinde DI container a AddHttpClient dememi sağlayan ve HttpClientFactory devreye sokan extension metodun geldiği paket. Bu noktadan itibaren de artık bir core web app e eklediğim gibi ClientFactory yi ekleyebilirim.

Bunun için bir IServiceCollection field ını App.cs tarafında oluşturdum. Eğer null ise Constructor new ServiceCollection diyerek Container ı oluşturuyorum.

Daha sonrada uygulama sayfalarımdan burada register etmiş olduğum service lere ulaşmak için IServiceProvider property mi oluşturuyorum. Bunu ayağa kaldırmak içinde tüm serviceleri register ettikten sonra BuildServiceProvider() diyerek provider objemi oluşturuyorum. App.cs de yapacaklarım bu kadar.

 public partial class App : Application
    {
        IServiceCollection services;
        internal static IServiceProvider ServiceProvider { get; private set; }
        public App()
        {
            InitializeComponent();

            if (services == null)
            {
                services = new ServiceCollection();
            }
            services.AddHttpClient();


            ServiceProvider = services.BuildServiceProvider();
            

            MainPage = new MainPage();
        }

         .....

App.cs de gerekli işlemleri bir kaç satır da hallettikten sonra, ki bu birkaç satırda şunları yapmış olduk;

  • Bir DI mekanizması oluşurup IoC container kurduk
  • Bu Container a IHttpClientFactory yi register ettik

artık MainPage e geldiğimde, aşağıdaki gibi ServiceProvider üzerinden ilgili service ime ulaşabilirim. Burada ulaşmak istediğimiz service de IHttpClientFactory nin kendisi.

 public partial class MainPage : ContentPage
    {
        IHttpClientFactory _clientFactory;

        public MainPage()
        {
            InitializeComponent();

            _clientFactory = App.ServiceProvider.GetRequiredService<IHttpClientFactory>();

            Task.Run(async () =>
            {
                var result = await _clientFactory.CreateClient().GetStringAsync("http://randomuser.me/api?results=1");

            });
        }
    }

 _clientFactory = App.ServiceProvider.GetRequiredService(); dedikten sonra artık bu clienFactory normal bir asp.net core projemdeki gibi elimde. Burada basic usage olarak kullandığımız için hiçbir parametre verdeden CreateClient diyerek HttpClient ımızı oluşturup gerekli işlemlerimizi bunun üzerinden yapabiliriz.

Görmüş olduğunuz gibi Microsoft un Asp.Net Core ile beraber kurmuş olduğu yapı oldukça esnek ve birbiri ile kullanılmaya oldukça müsait. Bir xamarin projemizde HttpClientFactory yi ve hatta Microsoft kendi micro Container ını kullanmış olduk.

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

Asp.Net Core Birden Fazla DelegatingHandler Kullanma ve Request LifeCycle

Selamlar,

Bir önceki yazımda tanıtmış olduğum DelegatingHandler ların kullanımı ve uygulamamızda clienttan gidecek olan requestlerin lifecycle ı arasına nasıl girdiğini görmüştük.

DelegatingHandler ların çalışma yapısı olarak Asp.Net Core Middleware lara benzediğinden bahsetmiştik. Yani request in lifecycle ına eklediğimi sıra ile request server tarafına gitmeden önce çalışırlar, response dönüşünde de ters sıra ile çalışıp client a geri dönerler. (Tabi herhangi bir delegatinghandler ile request akışını kesip bir sonrakilere ulaşmdan biz kendimiz de geriye bir httpresponsemessage dönebiliriz, bir önceki örneğimizde bunu görmüştük)

Aşağıdaki resimde görsel olarak bir request response akışı sırasında DelegatingHandler ların ne şekilde akışa dahil olduğuna bir bakalım.

Capture

Resimde gördüğümüz gibi asıl HttpHandler a request ulaşmadan önce, bizim request in pipeline ına eklediğimiz sıra ile tüm delegatinghandler lar dan request imiz geçerek ilerliyor, response alındıktan sonrada ilk önce HttpClientHandler lar dan sonra eklediğimiz sıra ters yönde geri dönüyor.

Peki biz de birden çok delegating handler örneği kullanımına bir bakalım.

Bir önceki örneğimizde AuthTokenHandler dan başka olarak bir de loglama ve Timing i kontrol etmemiz için bir de Timer Handler ekleyelim.

Timing DelegatingHandler ımız şu şekilde;

public class TimingHandler : DelegatingHandler
{
    private readonly ILogger _logger;

    public TimingHandler(ILogger logger)
    {
        _logger = logger;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        var sw = Stopwatch.StartNew();

        _logger.LogInformation("Starting request");

        var response = await base.SendAsync(request, cancellationToken);

        _logger.LogInformation($"Finished request in {sw.ElapsedMilliseconds}ms");

        return response;
    }
}

AuthTokenHandler ımız da aşağıdaki gibi

  public class AuthTokenHandler : DelegatingHandler
    {
        private readonly string _token;
        private const string TOKEN = "Authorization";
        public AuthTokenHandler(string token)
        {
            _token = token;
        }

        protected override async Task<HttpResponeMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.Headers.Contains(TOKEN) && string.IsNullOrEmpty(_token))
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new StringContent("Missing auth token.")
                };
            }
            else if (!request.Headers.Contains(TOKEN) && !string.IsNullOrEmpty(_token))
            {
                request.Headers.Add(TOKEN, $"Bearer {_token}");
            }

            var response = await base.SendAsync(request, cancellationToken);

            return response;
        }
    }

Log Handler ımız da aşağıdaki gibi.

public class LogHandler : DelegatingHandler
    {
        private readonly ILogger _logger;

        public LogHandler(ILogger logger)
        {
            _logger = logger;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var content = await request.Content.ReadAsStringAsync();
            Debug.Write($"{Environment.NewLine} ApiLog Request Path: {request.RequestUri.AbsolutePath}");
            Debug.Write($"{Environment.NewLine} ApiLog Request Uri: {request.RequestUri.AbsoluteUri}");
            Debug.Write($"{Environment.NewLine} ApiLog Request Query: {request.RequestUri.Query}");
            Debug.Write($"{Environment.NewLine} ApiLog Request Form Data: {content}");

            var response = await base.SendAsync(request, cancellationToken);

            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                var responseContent = await response.Content.ReadAsStringAsync();

                Debug.Write($"{Environment.NewLine} ApiLog Response Data: {responseContent}");
                Debug.Write($"{Environment.NewLine} ApiLog Response status code: {response.StatusCode}");
                
            }

            return response;
        }
    }

Tüm handlerlarımızı tanımladıktan sonra, hepsini AspNet Core un dependencyinjection container ına ekleyip aşağıdaki gibi request in pipeline ına çalışmasını istediğmiz sırası ile ekliyoruz.

   public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddTransient<AuthTokenHandler>();
            services.AddHttpMessageHandler<TimingHandler>();
            services.AddHttpMessageHandler<LogHandler>();
            services.AddHttpClient("randomuserclient", client =>
            {
                client.BaseAddress = new Uri("https://randomuser.me/api");
                client.DefaultRequestHeaders.Add("Accept", "application/json");
                client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
            })
            .AddHttpMessageHandler<TimingHandler>();
            .AddHttpMessageHandler<AuhTokenHandler>();
            .AddHttpMessageHandler<LogHandler>();


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

 

Aşağıdaki gibi normal client ımızı kullanıyoruz.

public class HomeController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
        public HomeController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task Index()
        {
            var client = _httpClientFactory.CreateClient("randomuserclient");
            var result = await client.GetStringAsync("?results=5");
            return View();
        }

Burada işleyiş nasıl olacağını bir özetleyelim

  • Request önce TimingHandler ımıza gelecek, Burada stopwatch çalışacak ve base.SendAsync dediğimiz noktadan itibaren kodun devamı çalışmadan bir sonraki handler ımıza geçecek
  • Pipeline eklediğimiz sıradaki handler ımız olan AuthToken Handler ımıza request gelecek. Burada Header kontrolü yapılıp yeni token değeri request in header ına eklenecek ve yine base.SendAsync dediğimiz anda bir sonraki handler ımıza geçecek
  • Request in pipeline ına eklediğimiz son handler olan Loghandler a request gelecek. burada da base.SendAsync dediğimiz andan itibaren artık request HttpClientHandler a gelecek ve request artık bizden çıkıp ilgili API ye gidecek
  • Sonrasonda HtpResponseMessage  ı aldığımızda, HttpClientHandler response u karşıladıktan sonra ilk olarak pipelindaki son handler ımız olan LogHandler ın base.SendAsync kısmındaki sonraki kodlar çalışacak
  • Buradaki kodlar tamamlandıktan sonra bir önceki handler olan AuthToken Handler a response gelecek ve yine base.SendAsync dan sonraki kodlar çalışacak.
  • Bu kısım da bittikten sonra ilk handlerımıza response gelecek ve yine base.SendAsync kısmındaki sonraki kodlar çalışacak ve en nihayetinde request i attığımız caller metoda response ulaşacak.

 

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

Asp.Net Core 2.0’a Giriş – Bölüm 1 (NewProject, Solution Structure, Solution File hakkında)

Selamlar. Bu yazı serimizde AspNet Core’a bir girizgah yapacağız. Basit bir örnekle biraz yüzeysel olarak AspNet Core‘a  aşina olmuş, AspnetCore tarafında kod yazmaya başlayarak işin daha derinine inmeden önce biraz ellerimizi ısıtmış, AspnetCore tarafında biraz teorik bilgi sahibi olmuş olmak bu yazı serisinin temel amacı olacak. Sonraki yazılarda kısım kısım daha teknik detaylara girmeye çalışacağım.

Bu seri ve sonraki tüm yazılarda Visual Studio 2017 kullanacağım. Bu yüzden herhangi  birşeye başlamadan önce, daha sağlık ilerlemek adına Visual Studio 2017 yi ve Asp.Net Core 2‘yi download edip pc nize yüklerseniz sorunsuz bir şekilde blogları takip edebilirsiniz.

  • .NetCore un engüncel SDK sını şuradan indirebilirsiniz. Sadece SDK yı indirip kurmanız yeterli olucaktır, zaten Core Runtime, SDK içerisinde gelecek.
  • Visual Studio 2017 yi download etmek ve kurulum ile ilgili detaylı bilgi almak için buradan faydalanabilirsiniz.

Asp.Net Core Microsoft tarafından en baştan geliştirildi. Bizde şimdi Visual Studio 2017 mizi açıp sıfırdan bir Asp.Net Core projesi oluşturalım ve her şeye sıfırdan bakıp anlamaya çalışalım.

Klasik olarak başlangıç noktamız File -> New Project seçtikten sonra, ister VisualC# altından, ister .NetCore altından isterseniz de Web sekmeleri altında Asp.Net Core Web Application proje tipini seçip, projemize bir isim verdikten sonra Template seçme adımına geliyoruz.corenewprj

Yukarıdaki gibi projemizin tipini seçip, ismini verip, pc de saklayacağımızı yeri belirledikten sonra aşağıdaki Template seçme adımına geliyoruz. Herşeye sıfırdan bakmak istediğimiz için WebApplication veya WebApplication (Model-View-Controller) gibi birçok hazır dosya ve kodlar ile gelen template i değil de Empty template i seçerek yolumuza devam edelim. emtpyproj

Empty Template i seçip, herhangi bir authentication mekanizması seçmeden projemizi oluşturuyoruz. Şu ana kadar yaptımız tüm bu adımları Visual Studio yerine .NetCore CLI‘dan da yapmak mümkün biliyorsunuz. Ama CLI(Command Line Interface) detaylarına sonra gireceğiz. Yine de basitçe CLI dan nasıl proje oluşturulup, birbirine referans verilip, çalıştırılır abakmak isterseniz, önceki yazımda anlanttığım, .NetStandard 2.0 proje oluşturup bir Web app den kullanma örneği yaptığım yazıma bakabilirsiniz.

Peki, projemizi oluşturduk gelelim Solution Structer ın nasıl değiştiğine, düzenlendiğine. Yeni ve sıfırdan oluşturduğumuz projemizin structure ı aşağıdaki gibi görünüyor.

solstructs

Eğer daha önceki .NetCore versiyonları ile çalıştıysanız, ilk fark edeceğiniz şey project.json doyasının artık olmadığı. Bunun sebebi bu dosyanın MSBuild ile uyumlu çalışmaması, ve MSBuild in birçok DevOps Environment‘ı tarafından kullanılıyor olması. Bunun yerine artık doğrudan, hiç projeyi unload etmeden, csproj (VB ile yazıyorsanız vbproj) dosyasını editleyebiliyor olmamız. proje dosyası (csproj-vbproj) herhangi bir folder veya file referansı içermiyor artık. Solution File Explorer da açıp içerisi boş bir txt dosyası ekleyelim ve bakalım ne olacak.

Gördüğünüz gibi proje klasörüne eklenen text dosyası doğrudan proje içerisine included edilmiş şekliyle geldi. Bunun sebebi Asp.Net Core, Solution Explorer ın pc deki Disk ile birebir map lenmiş olması. Yani bir dosyayı projeye eklemek istediğimizde doğrudan Solution klasörüne atmak artık yeterli, Folder veya File referansları ile alakamız yok. Bu da aslında bir Source Control mekinizması kullanırken ki sıkça karşımıza çıkan, projeye yeni bir şeyler eklendiğinde, ilgili klasörün folder-file referanslarının da projeye eklenmesi yüzünden yaşadığımız conflict lerden bizi kurtarıyor.

Birde proje dosyamızın içine bakalım.

csprojfl

görmüş olduğunuz gibi proje dosyası gayet net ve temiz. En üstte projeyi oluştururken seçmiş olduğumuz Asp.Net Core framework ünün takma ismi (tüm platformların kendi takma ismi var, ör: netstandard2.0, netcoreapp2.1, net461, net471 vb..)

Bir sonraki Item a bakalım. wwwroot klasörü. Classic AspNet projelerinde Root Folder daki tüm dosyalar sunulmaya hazır halde olurdu. Browserdan ilgili folder ve file a gittiğinizde eğer o dosyaı Blacklist e alıp blocklamadıysak, WebConfig, Global.asax gibi hassas dosyalarda browserdan ulaşılabilir oluyordu.

Asp.Net Core da ise bu Blacklisting bakış açısı değişti ve WhiteListing e döndü. Artık bu wwwroot klasörü altında olmayan hiçbir şey doğrudan ulaşılmaz halde. Css, Javascript dosyaları, resimler gibi, browser ın ulaşması gerken tüm dosyalarımı koyacağımız yeni yer artık bu wwwroot klasörü olacak. Proje klasörüne gidip wwwroot ltında img  diye bir klasör oluşturup içine bir adet resim atalım.

imgroot

 

Solution kalsöründe wwwroot altında img klasörümüz ve içerisindeki resim anında Solution Explorer da görünür oldu. Şimdi bunu browserdan test etmek için projeyi çalıştıralım. Ama şöyle ekstra bir adım atmamı lazım, Sıfırdan bir template dosyası oluşturduğumuz için, Startup.cs içerisinde nerdeyse hiçbir built-in gelen middleware eklenmemiş halde. Startup dosyasından, Middleware yapısından ilerde detaylıca bahsedeceğiz ama şuan bu projeye eklediğimiz resmi görmemiz için Startup.cs doyasına gidip, Configure metodunun içerisine şu satırı eklememiz gerekiyor.

app.UseStaticFiles();

Şimdi Browserdan ilgili klasöre gidelim ve resim karşımızda.

coreimg.PNG

Peki proje dosyasındaki son item a bakalım.

<PackageReference Include=”Microsoft.AspNetCore.App” />

Normalde bir projeye nuget ten bir paket indirdiğinizde referansı buraya eklenirdi. Asp.Net Core 1.0 dan da hatırlarsanız, herbir paket ayrı ayrı projeye indiriliyordu, bu ilk başta güzel gibi gözüksede sonradan sorun çıkarmaya başladığı görüldü. Bu yüzden artık yeni bir proje oluşturduğunuzda içeriye versiyon numarası olmayan bu paket yükleniyor. Versiyon numarası yok çünkü siz .makinanızda NetCore her update ettiğinizde bu paket te güncelleniyor.

Microsoft.AspNetCore.App paketinde neler var?

  • Asp.Net Core un kullandığı tüm paketler
  • Entity Framework Core a ait paketler
  • Asp.Net Core ve Entity Framework Core un kullandığı tüm 3rd party paketler

Dolasyısı ile projenize neredeyse başka paket eklemeden geliştirmeye başlamaya hazırsınız. Peki bu kadar paket deploy zamanı ne oluyor? 🙂 Tabii ki bunu da düşünmüşler, yeni .NetCore Runtime Store fetarure ile kullanılmayan paketler deploy edilmeden ayıklanıyor.

Bu yazıyı burada tamamlayalım ve bir sonraki yazımızda, Program.cs ve Startup.cs dosyalarından devam edelim. Yeni bir Asp.Net Core Web Application nasıl oluşur ve Solution structer ı nasıldır öğrenmiş olduk. Serinin diğer yazılarında biraz daha aktif kod yazmaya başlayıp, yine biraz teoriğe değineceğiz.

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,