Erhan Ballıeker

Resilient Network Services Bölüm 2 – REFIT – 2

Selamlar. Bir önceki yazımda Resilient Network Services için kullanacağımız kütüphanelerden olan Refit için incelemelere başlamıştık. Bu yazımızda bunu tamamlayıp bir sonraki yazılarımız da diğer kütüphanelere değineceğiz.

Önceki yazımızı da Refit ile GET ve POST işlemlerine bakmıştık. Farklı durumlar için incelemiş örnekler göstermiştik. Okumadıysanız bu yazıdan önce Refit ile ilgili ilk yazımı okumanızı tavsiye ederim.

Bu yazımızda refit ile request in Header, Authorization, Multpipart upload, response ve error handling kısımlarına değineceğiz.

Static ve Dynamic Header Tanımlamaları

Static bir header ı refit interface imize ister tüm Interface bazında istersek de metod bazında istediğimiz kadar eklemek için, kullanacağımız yöntem Headers attribute u olucaktır. Örnek olarak;

[Headers("Content-Type: application/json")]
public interface IGitHubApi
{
    [Get("/users/{user}")]
    Task<User> GetUser(string user);
    
    [Post("/users/new")]
    Task CreateUser([Body] User user);
}

şeklinde global olarak(yani interface içerisindeki herbir metod için çalışacak şekilde) tanımlayabiliriz. Ya da aynı attribute u sadece istediğimiz metodun üzerine de yerleştirebiliriz.

Bu zaten çok önemli bir durum değil çok fazla da ihtiyacımız olucak değil, ama kullanımını yine de bilmekte fayda var. Daha önemlisi dinamik olarak değişecek olan headerlar için nasıl bir yöntem kullanacağız. Örneğin her bir request için Authorization header ı göndermek istiyorum. Bu header da dinamil bir header olucak tabii ki. Bunun için ise şu şekilde ilerliyoruz.

[Get("/users/{user}")]
Task<User> GetUser(string user, [Header("Authorization")] string authorization);

var user = await GetUser("erhanballieker", "mytoken.....");

Yukarıda göreceğiniz gibi interface içerisinde tanımlamış olduğumuz metodun header olarak göndermek istediğimiz parametresinin başında Header attribute u nu kullanıp parametre olarak header ın adının ne olacağını veriyoruz(örnekte “Auhtorization” dedik).  bu metodu kullanırken de 2. satırda yazmış olduğumuz gibi “mytoken…” olan paraametre request in Headerlarına Authorization header ı olarak ekleniyor olacaktır.

Refit ile request in headerlerı ile oynamakta bu kdar basit hale gelmiş durumda. Son olarak header lar kısmını bitirmeden şundan bahsedelim, Headerları tanımalamak için 3 yer gördük interface üzerinde, metod üzerinde ve parametre üzerinde, peki bunlardan hangisi daha öncelikli. yani aynı header ismi ile üçünede bir değer koysak hangisi kazanır,

öncelik sırası şöyle;

  1. Parametre başına tanımlanan değer.
  2. metod başına tanımlanan değer
  3. interface seviyesinde tanımlanan değer.

yani aşağıdaki gibi bir örnek için request e “MyHeader” ismiyle eklenecek olan değer parametre olarak verilen değerinki olucaktır.

[Headers("MyHeader: MyHeaderInterface")]
public interface IGithubApi
{
   [Headers("MyHeader: MyHeaderMetod")]
   public Task GetUser(string username, [Header("MyHeader")] string parameter = "actaulValue"]);
}

Multipart Upload

[Multipart] attribute u ile işaretlemiş olduğumuz metodlar multipart content type olarak submit olucaklar.desteklenen multipart methods parametre tipleri aşağıdaki gibidir.

  • string (parameter name will be used as name and string value as value)
  • byte array
  • Stream
  • FileInfo

parametrenin adı multipart datanın adı olarak set edilir default olarak. Yine AliasAs kullnarak bu durumu değiştirebiliriz.

public interface ISomeApi
{
    [Multipart]
    [Post("/users/{id}/photo")]
    Task UploadPhoto(int id, [AliasAs("myPhoto")] StreamPart stream);
}

Bu metoda multipart olarak data göndermek için aşağıdaki şekilde kullanabiliriz.

someApiInstance.UploadPhoto(id, new StreamPart(myPhotoStream, "photo.jpg", "image/jpeg"));

Response Handling

Refrofitin aksine Refit te synchronous api call yapamıyoruz. Tüm requestler Task ile veya IObservable ile async olarak yapılmalı.  Generic parametresi verilmemiş bir Task olarak metodu tanımlamak sadece request in başarılı tamamlanıp tamamlanamadığını bize söyler o kadar.

[Post("/users/new")]
Task CreateUser([Body] User user);


await CreateUser(someUser);

Eğer parametre tipi HttpResponseMessage veya string ise content olduğu gibi geriye döner. Task objesi içerisine bir model verirsek dönen response Refit tarafından otomatik olarak istediğimi model e deserialize edilir ve o şekilde bize ulaşır.

//response string olarak geri döner. (ham json data gibi)
[Get("/users/{user}")]
Task<string> GetUser(string user);

//tüm response IObservable olarak Reactive Extensionlar ile kullanıma hazır şekilde döner.
[Get("/users/{user}")]
IObservable<HttpResponseMessage> GetUser(string user);

//response verilen generic tipe dönüştürülüp bize ulaşır.
[Get("/users/{user}")]
Task<User> GetUser(string user);

Genel olarak bir api ile haberleşirken herhangi bir modelin CRUD işlemlerini yapacak endpoint e request atmamız gerekecektir. Bunun için her model için ayrı ayrı interface veya metodlar tanımlamak yerine, Refit bize generic olarak interfaceleri tanımalama imkanı da sunuyor.  Aşağıdaki örnekte ki gibi tüm Refit interface imizi generic bir parametre tipine bağlı olarakta tanımlayabiliriz.

public interface ICrudApi<T, in TKey> where T : class
{
    [Post("")]
    Task<T> Create([Body] T payload);

    [Get("")]
    Task<List<T>> ReadAll();

    [Get("/{key}")]
    Task<T> ReadOne(TKey key);

    [Put("/{key}")]
    Task Update(TKey key, [Body]T payload);

    [Delete("/{key}")]
    Task Delete(TKey key);
}

Yukarıda göreceğiniz gibi projemizde ki herbir modelin crud işlemleri için bu şekilde bir Refit interface i tanımlayabiliriz. Herbir request için ilgili HttpVerb ü verilip, Task içerisinide geri dönen response un deseriazlize edilecek generic type veriliyor.

Son iki yazımızda Refit e oldukça değinmiş incelemiş olduk. Bir sonraki yazımızda Resilient Network Services serimizde Fusillade in kullanımı ile devam ediyor olacağız.

Görüşmek üzere.

Microsoft Xamarin Istanbul Development Meetup “Xamarin Resilient Network Services Bölüm 1”

Herkese Selamlar,  biraz geç olsa da 25 ekim günü Microsoft Türkiye‘de yaptığımız etkinlikteki konuyla ilgili yazma fırsatı ancak bulabiliyorum. Güzel bir etkinlik günü geçirdik, 3 farklı değerli arkadaşımla beraber aşağıdaki konulara değinmiştik.

• .Net Core ile Dependency Injection (Özgür Kaplan)
Xamarin ile Dependency Injection (Yiğit Özaksüt)
Xamarin ile Resilient Network Services (bunu ben anlattım ve burada da uzunca değineceğim detaylarına.)
C# hakkında doğru bilinen yanlışlar ve performans ipuçları (Cihan Yakar)

Benim anlattığım Xamarin ile Resilient Network Services sunumum ile ilgili proje ve dosyaları buradan inceleyebilirsiniz.

Katılan dinleyici ve konuşmacı tüm arkadaşlara teşekkür ederek kendi konumun detaylarına giriş yapıyorum.

Bu konu ile ilgili aslında yazmak istediğim birkaç blog serisi var. Aslında genel bakış açısını burada belirtip detaylarına diğer blog postlarımda değineceğim.

İlk olarak şu Resilient(Esnek) kelimesinden başlayalım. Ne demek bir yapının esnek olması. Bu aslında şu demek;

  • Kurduğumuz yapı, belli hatalar karşısında nasıl davranacığını önceden bilen, öncelikleri belli ve değiştirilebilir esnek kırılmaz bir yapı olmalı.

Bu yapı, diğer tüm yazılım projelerinde, uygulamayı mümkün olduğunca sağlıklı bir şekilde ayakta tutmak için kullanılan mimari bölümlerin(Ölçeklenebilir olması, esnek olması, hızlı olması, güvenilir olması vs vs..) küçük bir parçası aslında.  Bu sistemin ben yazılım projelerinin olmaz ise olması Networking üzerinde anlattım, çünkü danışmanlıklarımda gördüğüm ve eğitimler de hissettiğim en büyük açık burada mevcut idi.


  using (var client = new HttpClient())
  {
    client.BaseAddress = new Uri("https://randomuser.me/api/");
    var content = await client.GetStringAsync("?results=10&page=1");
    var result = JsonConvert.DeserializeObject<ResponseModel>(content);
  }

Yıkarıda ki kod bloğunu bir bakalım. Birçok projede bir API haberleşmesi için gördüğüm kod bu kadar da kalıyor. Daha üzerine düşünülmüş olanlarda ise bu şekilde bir kullanım generic bir class içerisine yerleştirilmiş ve onun içerisinden haberleşme çağırılıyor oluyor. Fakat içeriği pek de değişmiyor. Peki bu koddaki yanlış nedir? Yada var mıdır?

Benim gördüğüm kadarı ile şöyle;

Koda ilk bakışta bir yanlış gözükmüyor olabilir. Evet derlenir ve çalışır da, hatta herşey iyi giderse response u alıp gerekli çevirme işlemlerini de yapar. Ama işte bu kod bloğunda birçok şey yolunda gider varsayılmış. Http client objesinin static tanımlanmamış olması ve bunun sıkıntılarına başka zaman değineceğim bunu şimdilik göz ardı edelim diğer konulara bakalım.

  • Internette bir sorun olduğunda bu request ne olacak
  • Server dan 500 aldığında request im ne yapmalı,  503 olduğunda ne olmalı.
  • Time out yersem ne yapmalıyım? yeniden göndermeli miyim requesti? Eğer gönderecek isem kaç kere göndermeliyim, ne kadar ara vermeliyim?
  • Response cachlenebilir bir response mu eğer öyle ise bunu da devreye koysam güzel olmaz mı?
  • Bu request uygulama içerisinde gidecek olan diğer requestlere göre bir farkı önceliği var mı?

gibi gibi sorular aklıma geliyor. İşte tüm bu konular üzerinden bir xamarin projesinde kullanmamız ve güzel bir networking altyapısı kurmanız için size 5 farklı ve değerli kütüphaneden bahsedeceğim. Ve bunların hepbirden nasıl uyumlu olarak kullanabiliriz buna değineceğim.

Kütpühaneleri şunlar;

  • Refit: The automatic type-safe REST library for .NET Core, Xamarin and .Net
  • Fusillade: An opinionated Http library for mobile development
  • ModernHttpClientPlatform spesific networking libraries to Xamarin applications
  • Akavache: An Asynchronous Key-Value Store for Native Applications
  • Polly: .Net resilience and transient-fault-handling  library

kısaca açıklamlarını direk kendi github adreslerindeki gibi yazdım kenarlarına. Detaylarına ve örneklerine sırası ile diğer postlarımda başlayacağım. Hepsinden bahsettikten sonra da bir arada bir xamarin projesinde nasıl kullanılırız buna bakacağız.

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

 

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.

Xamarin.Workbook ile UrhoSharp Örnekleri Bölüm 1.

Herkese Merhaba,

Bundan önce UrhoSharp’ın temellerinden, Xamarin Workbook‘tan bahsetmiştim. Bu yazıdan önce UrhoSharp Giriş ve Xamarin Workbook Giriş yazılarımı okumanızda fayda var. Bu yazımda biraz daha yoğun görsellik içeren bir örnek yapacağım. UrhoSharp’ta Shapes, Actions ve Physics namespace’lerinden bahsedeceğim. Yeni bir workbook açıp (Console .Net Framework) UrhoSharp paketini nuget package manager’dan indiriyoruz.

Önce örneğimizde kullanacağımız namespacelerimizi workbook’umuza ekliyoruz.

using Urho;
using Urho.Shapes;
using Urho.Actions;
using Urho.Physics;

Şimdi daha önceki örneklerimizde görmüş olduğumuz SimpleApplication sınıfı yardımı ile 3D canvas’ımızı oluşturuyoruz.

 var app = SimpleApplication.Show(new ApplicationOptions{
             Width = 750,
             Height = 750
           });

Canvas’ımız oluştuktan sonra, SimpleApplication nesnesinin bizim için oluşturduğu CameraNode’unun Position ve Direction property’lerini değiştirelim.

app.CameraNode.Position = new Vector3(0, 0, -5);
app.CameraNode.SetDirection(new Vector3(0, 0, 1));

SimpleApplication’ın oluşturduğu RootNode’un Position değerinin default’da nasıl oluştuğuna bakalım.

possss

Gördüğünüz gibi RootNode (0,-2,8) koordinatlarına eklenmiş. Bunu Origine taşıyalım.

 app.RootNode.Position = new Vector3(0,0,0);

Şimdi SimpleApplication’ın oluşturduğu CameraNode ve bu node’a eklediği light’ı silip biz kendimiz oluşturalım. Light component’imi direk Scene’e tanımlamak istiyorum. Hatırlatayım Light tipi default olarak Point tanımlanırdı ama biz bunu Directional olarak tanımlayalım.

// Camera Node a ait tüm child node ları siliyoruz
app.CameraNode.RemoveAllChildren();

// directional light'ı Scene'e eklemeden önce kaldırıyoruz. Hatırlarsanız bu işlemleri workbook taki her bir hücrenin birden çok kez çalışması sebebi ile, duplicate node'lar oluşturmamak için yapıyorduk.
app.Scene.RemoveChild(app.Scene.GetChild("directionalLight"));
Node lightNode = app.Scene.CreateChild("directionalLight");

// Light Node'uma Light Componenti ekliyorum.
Light light = lightNode.CreateComponent();
light.LightType = LightType.Directional;
light.Color = new Color(0.65f, 0.65f, 0.65f);

// 3D sahnelerde genellikle ışık, oyuncunun sol omzunun üzerinden geliyormuş gibi bir yönde set edilir. Bunun için,aşağıdaki şekilde bir Vector3 tanımlıyorum.
lightNode.SetDirection(new Vector3(2, -3, 1));

// Simple Application tarafından oluşturulan Zone nesnesinin Ambient color rengini veriyorum. Bunun light componentinin Color değeri ile toplamının 1'i geçmemesine dikkat edin. Aksi halde normalden fazla parlak bir sahneniz olacaktır.
app.Zone.AmbientColor = new Color(0.35f, 0.35f, 0.35f);

Oluşan 3D canvas’da mouse ile kamerayı hareket ettirebilme özelliği vardır. Mouse’ın sol tuşuna basılı tutarak hareket ettirirseniz camera’nın oynadığını farkedeceksiniz. Ben cameranın oynamasını değil, Mouse’u hareket ettirdiğimde 3D ortamı daha iyi anlamak için RootNode nesnemi Rotate etmek istiyorum.

//Önce Mouse hareketiyle cameranın oynamaması için aşadağıki SimpleApplication nesnesinde aşağıdaki değişikliği yapıyorum
app.MoveCamera = false;

Şimdi mouse’un sol tuşuna basılı halde hareket ettiğimde, RootNode’u hareket ettirecek event handler’ı oluşturacağım. Sonrasında da bu event handler SimpleApplication objesinin Update event’ine atayacağım. Bu update metodu, UrhoSharp’ın Game Loop’unda tetiklenen event’dir. Normal bir UrhoSharp application’da OnUpdate metodunu override ederek de ilerleyebilirsiniz. Bu metot sahnedeki obje ve animasyon yoğunluğuna bağlı olarak saniyede pek çok kere çalışabilmektedir.

//Update event ine atayacağım event handler ı tanımlıyorum.
void ProcessMouseMovement(UpdateEventArgs args)
{
    //Gelen input Mouse un sol tuşu ise..
    if (app.Input.GetMouseButtonDown(MouseButton.Left))
    {
        //Mouse un X ve Y koordinatları ile 2 boyutlu bir vektör tanımlıyorum. Y değerine, mouse un Y değerinin negatifini set ediyorum, çünkü mouse u yukarı oynattığımda aşağıyı, aşağı oynattığımda yukarıyı göstermek istiyorum.
        Vector2 mouseMove = new Vector2(app.Input.MouseMove.X, -app.Input.MouseMove.Y);
        
        //Her bir piksel için 1 derece kabul ediyorum
        float angle = mouseMove.Length; 

        if (angle > 0)
        {
            //Aşağıda bir vector3 tanımlıyorum, Z her zaman 0, fakat RootNode a döndürme hareketi yaptıracağım için, X ekseninde mouse u hareket ettirdiğim Y ekseni etrafında döndürüyor olucam bu yüzden X değerine mouse hareketinin Y değerini veriyorum, aynı şekilde de Y değeri yerine mouse hareketinin negatifi X değerini veriyorum.
            Vector3 axis = new Vector3(mouseMove.Y, -mouseMove.X, 0);
            app.RootNode.Rotate(Quaternion.FromAxisAngle(axis, angle), TransformSpace.Parent);
        }
    }
}

Tekrar workbook’taki her bir hücrenin tekrar tekrar çalışabilir olmasından dolayı biraz reflection ile SimpleApplication’ın Update event’ini kaldırıyorum.

using System.Reflection;
FieldInfo field = typeof(Application).GetField("Update", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(app, null);

//Event i kaldırdıktan sonra tekrar Upadte event ine hazırldağım event handler set ediyorum.
app.Update += ProcessMouseMovement;

Peki, Canvas’ımızı oluşturduktan sonra oldukça hazırlık kodu yazdık. Birkaç farklı şeyi de irdelemiş olduk. Artık düz bir çizgiden fazlasını yapmaya başlayalım. Önce kod ile ekrana bir üçgen çizelim. Scene’deki 3 boyutu daha iyi algılamamız için tüm eksenler yönünde farklı renklerde çizgiler çizelim. Sonrasında da Urho.Shapes namespace’i ile neleri daha kolay yapabiliriz bir bakalım.

 //üçgenin noktalarını bir VertexBuffer.PositionNormalColor dizi olarak tanımlayalım.
VertexBuffer.PositionNormalColor[] vertices = {
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(-3,0, 5),
 Color = Color.Blue.ToUInt()
 },
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(0 ,3, 5),
 Color = Color.Yellow.ToUInt()
 },
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(3, 0, 5),
 Color = Color.Red.ToUInt()
 },
};
// Z eksenine göre 5 değerinde, yani ekrandan 5 birim uzaklıkta, Tabanı X ekseninde ve yüksekliği Y eksenin 3. birimde olacak şekilde 3 vertex tanımladım.

Şimdi VertexBuffer objemizi oluşturalım.

 var vertexBuffer = new VertexBuffer(Application.CurrentContext, false);
vertexBuffer.SetSize((uint)vertices.Length, ElementMask.Position | ElementMask.Normal | ElementMask.Color);
vertexBuffer.SetData(vertices);
//Bu sefer oluşturduğum vertex in Color değerinide kullandığım için, Position ve Normal flag leri yanında ElementMask.Color flag ini de vertexBuffer ın Size ını set ederken, eklemem gerekiyor.

Şimdi Geometry objemizi oluşturup bu üç vertex’i, üçgen olarak birleştirmek istediğimi söyleyeceğim.

 var geometry = new Geometry();
geometry.SetVertexBuffer(0, vertexBuffer);
geometry.SetDrawRange(PrimitiveType.TriangleList, 0, 0, 0, (uint)vertices.Length, true);
//SetDrawRange metodu ile bu iç noktayı TriangleList olarak birleştirmek istediğimi söyledim. Yani vertices dizimde 9 vertex im olsa idi, her bir üçlü bir üçgen oluşturacaktı.

Sırada bu geometriyi kullanacak Model objemizi oluşturmak var.

 var model = new Model();
model.NumGeometries = 1;
model.SetGeometry(0, 0, geometry);
model.BoundingBox = new BoundingBox(new Vector3(0,0,0), new Vector3(1,1,1));

Şimdi Material objemi zioluşturucağım. Bunu yaparken yine bir bitmap kullanmayıp sadece bir renkten türeyen Material nesnesi oluşturacağım.

 var material = Material.FromColor(Color.Green);

Artık bu model ve material objelerini birleştireceğim static model nesnemizi yaratmam ve bu nesneyi ekranda bir node’a eklemem gerek. Şimdi bunu yapalım. Node’u eklemeden önce silmeyi unutmayalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("triangleNode"));
//GetChild metodu ilgili node u bulamazsa null dönecek ama bu RemoveChild metodu için sorun değil, işlem doğrudan ignore edilir.

//Node u oluşturuyorum.
var triangleNode = app.RootNode.CreateChild("triangleNode");

//Static Model componentimi oluşturuyorum.
var triangle = triangleNode.CreateComponent();

//model i set edelim.
triangle.Model = model;

//material ı set edelim.
triangle.SetMaterial(material);

tşranglecolor
Tüm bu işlemlerden sonra ekranda yeşil bir üçgen gördük. İstersek mouse’un sol tuşuna basılı tutarak RootNode’un rotate edip üçgenimizin etrafını gezebiliriz. Fakat bir şeye dikkat ettiniz mi? Vertex’leri tanımlarken her birine birer Color atamıştım, fakat en sonunda Material oluştururken verdiğim Yeşil renk bu değerleri ezmiş oldu. Bunu ortadan kaldırmak için yapmam gereken Material bir Technique set etme olacak.

//NoTextureVCol değerindeki NoTexture bir bitmap kullanma demek, VCol kısmı ise Vertex Color kullan demek.  
material.SetTechnique(0, CoreAssets.Techniques.NoTextureVCol, 1, 1);

//Bu kodu çalıştırdığımda vertexlerin renklerininde olaya dahil olduğunu göreceğim.Fakat yeşil rengi ortadan kaldırmak için Material'ı oluştururken renksiz bir instance oluşturmak gerek. Yukarıda Materail ı tanımlarken bu değişikliği yapıp tekrar deneyip görmenizi öneririm.Bunu yaptığınızda tamamen Kırmızı sarı ve mavi renklerinin karışımı resimdeki gibi bir üçgen göreceksiniz.

Şimdi yukarıda bahsettiğimiz gibi, tüm eksenler boyunca farklı renklerde çizgiler çizelim ki RootNode u hareket ettirdikçe 3D sahneyi daha rahat algılayabilelim. Bunun için her bir eksen yönünde -50, +50 aralığında çizgiler çizmemiz yeterli olacaktır.

//Tüm vertexleri ekleyeceğim listeyi oluştuyorum...
var lineVertices = new List();

-50, + 50 aralığında eksenleri çizelim demiştik. Şimdi bu döngüyü kurup vertex’lerimizi yukarıda tanımladığımız listeye ekleyelim ama bunun da öncesinde tüm eksenleri tanımladığım basit bir dizi oluşturalım. Sonuçta her bir eksen için, bu -50 +50 döngüsü çalışmalıdır. Döngünün sonunda elimde her bir eksen 100 tane totalde 300 adet vertices olmalı.

Vector3[] unitLineVertices = {Vector3.UnitX, Vector3.UnitY, Vector3.UnitZ};

Satırın çıktısında gördüğünüz gibi, her bir eksen bir birim vektör tanımlamak için oluşturulmuş UnitX, UnitY,  UnitZ field’larından yararlandım. Bu birazdan vector oluştururken de işimize yarayacak.

foreach(var unitVector in unitLineVertices)
{
    for(int i = -50; i < 50; i++)     
     {         
            //yeni bir vertex oluşturuyorum.        
            var lineVertex = new VertexBuffer.PositionNormalColor   
            {            
                 Position = i * unitVector, //int * birim vector = Vector3.. ör: i = 5 için Position = (1,0,0) * 5 => (5,0,0) olacak. 

            //X eksenindeysem kırmızı, Y eksenindeysem Yeşil, Z eksenindeysem mavi bir çizgi istiyorum.
            Color = unitVector == Vector3.UnitX ? Color.Red.ToUInt() : (unitVector == Vector3.UnitY ? Color.Green.ToUInt() : Color.Blue.ToUInt())
        };

        lineVertices.Add(lineVertex);
    }
}

Şimdi yukarıdaki sırayla gidelim (VertexBuffer -> Geometry -> Model -> Material -> Static Model -> Node). Bu sefer dikkat etmemiz gereken tek şey: Geometry objemizi oluştururken PrimitiveType olarak LineList kullanmak olacak.

var lineVertexBuffer = new VertexBuffer(Application.CurrentContext,false);
lineVertexBuffer.SetSize((uint)lineVertices.Count, ElementMask.Position |ElementMask.Normal |ElementMask.Color, false);
lineVertexBuffer.SetData(lineVertices.ToArray());

var lineGeometry = new Geometry();
lineGeometry.SetVertexBuffer(0, lineVertexBuffer);
lineGeometry.SetDrawRange(PrimitiveType.LineList, 0, 0, 0, (uint)lineVertices.Count, true);

var lineModel = new Model();
lineModel.NumGeometries = 1;
lineModel.SetGeometry(0, 0, lineGeometry); 
lineModel.BoundingBox = new BoundingBox(new Vector3(0,0,0), new Vector3(1,1,1));

var lineMaterial = new Material();
lineMaterial.SetTechnique(0, CoreAssets.Techniques.NoTextureVCol, 1, 1);

app.RootNode.RemoveChild(app.RootNode.GetChild("lineNode"));
var lineNode = app.RootNode.CreateChild("lineNode");

var line = lineNode.CreateComponent();
line.Model = lineModel;
line.SetMaterial(lineMaterial);

Yukarıda farklı birşey yapmadık. Bu kodu çalıştırdığımızda, istediğimiz 100 birimlik tüm eksenlerimizde farklı renklerde çizgiler çizmiş olduk. Üçgenimizin tam olarak Z eksenin +5 değerinde olduğunu daha rahat görebiliriz. Mouse ile biraz oynayarak sahnemizi gezebiliriz.

triangle2

Biraz da Urho’nun kendi Shape namespace’inden faydalanıp daha az kod ile daha fazla şey yapma vakti geldi. Şimdi origin’e bir adet küre ekleyelim. Bunun için Urho.Shapes namespace’in altındaki Sphere Component’ini kullanacağız. Sphere gibi 8 adet hazır shape mevcut. Biz bu örnekte hepsini görmeyeceğiz ancak sizin hepsini ekrana ekleyip görmenizde, el alışkanlığı kazanmanız fayda var.

//Direk olarak küremi koyacağım Node u üreterek işe başlıyorum.
app.RootNode.RemoveChild(app.RootNode.GetChild("sphereNode"));
var sphereNode = app.RootNode.CreateChild("sphereNode");

//StaticModel componenti yerine, bu component ten türeyen ama farklı özelliklere de sahip olan Sphere Componentini ekliyorum node'uma
var sphere = sphereNode.CreateComponent();
sphere.Color = Color.Blue;
//Bu componentin Color propertysi sayesinde direk renk set edebiliyorum. StaticModel Componentinde normalde böyle bir property yok. Material kullanıyordum renk bitmap vb set etmek için.

Vee bu kadar. Tam olarak orijinde, 1 birim çapında, mavi bir kürem oluştu bile.
Şimdi birde küp ekleyelim ve ona biraz animasyon katalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("boxNode"));

var boxNode = app.RootNode.CreateChild("boxNode");
var box = boxNode.CreateComponent();
box.Color = Color.Red;

//boxnode un position değerini değiştirelim.
boxNode.Position = new Vector3(3,3,3);

Ekranda kırmızı bir box nesnesini görebiliriz. Şimdi biraz animasyon katalım. Animasyonu yaratırken Node nesnesinin FiniteTimeAction objesini kullanacağız. Birden çok action’ı node’a set edebiliriz.

FiniteTimeAction boxAction = 
    new RepeatForever(
        //Rotate by action ı ile rotation animasyonunu ekliyorum.
        new RotateBy(duration: 5, //Animasyonun kaç saniyede tamamlanmasını istediğimi söylüyorum.
                     deltaAngleX: 0,
                     deltaAngleY: 360,//Y ekseni etrafında 360 derece dönecek ve sürekli çalışacak bir action oluşturuyorum.
                     deltaAngleZ: 0
        )
    );

//Önce tüm action ları siliyoruz.
boxNode.RemoveAllActions();

//Sonrada action'ı node a ekliyorum.
boxNode.RunActions(boxAction);

Şimdi de, 100 X 100’lük bir yüzey (düzlem) oluşturalım. Sonra da bu yüzeyin fizik kanunları ile etkilenmesi için rigid body ve collision shape’ini tanımlayıp bu düzlemimiz gibi bir de sphere oluşturup onu da yerçekiminden etkilenip yüzeye düşecek şekilde tasarlayalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("planeNode"));

var planeNode = app.RootNode.CreateChild("planeNode");
var plane = planeNode.CreateComponent();
plane.Color = Color.Green;

//100X100 lük olsun demiştik. Scale i değiştirelim.
planeNode.SetScale(100);

//Bu noktadan itibaren yeşil düzlemimiz canvasımızda belirdi.Şimdi rigidBody ve collision shape componentlerimiz tanımlayalım.
//RigidBody
var planeRigidBody = planeNode.CreateComponent();

//CollisionShape nesnesi, modelimizin diğer modeller ile etkileşim sınırlarını belirler.
var planeCollisionShape = planeNode.CreateComponent();
planeCollisionShape.SetStaticPlane(new Vector3(100,0,100), Quaternion.Identity);

Şimdi yerçekiminden etkilenecek ve yüzeye düşecek küremizi oluşturalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("rigidSphereNode"));
var rigidSphereNode = app.RootNode.CreateChild("rigidSphereNode");
rigidSphereNode.SetScale(2);
rigidSphereNode.Position = new Vector3(-3,5,2);

var rigidSphere = rigidSphereNode.CreateComponent();
rigidSphere.Color = Color.Yellow;

var sphereRigidBody = rigidSphereNode.CreateComponent();
sphereRigidBody.Mass = 1;

var sphereCollisionShape = rigidSphereNode.CreateComponent();
sphereCollisionShape.SetSphere(2, Vector3.Zero, Quaternion.Identity);

Yukarıdaki kod bloğunu da çalıştırdığımızda sarı bir kürenin ekranda belirip plane üzerine düştüğünü ve rootnode’ un rotasyonu olduğu yöne doğru kaydığını görüyoruz.triangle4

Yazıda oluşturduğum workbook’u buradan indirebilirsiniz.

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

Xamarin.Forms’da Kod Paylaşımı İçin .NET Standard 2.0 Kullanımı

Herkese Merhaba,

Bu yazımda önceden oluşturduğumuz veya yeni oluşturacağımız Xamarin.Forms projemizde, PCL (Portable Class Library) yerine nasıl .Net Standart 2.0 kullanabileceğimizden bahsedeceğim.

Bildiğiniz üzere, Visual Studio ile sıfırdan bir Xamarin.Forms projesi oluşturulduğunda bize iki opsiyon sunar. Bunlardan biri PCL diğeri de Shared Project. Shared Project, aslında File Linking gibi çalışan yazdığınız kodların tüm projelerin içine ayrı ayrı kopyalandığı ve paketlendiği bir kod paylaşım şeklidir yani SP ayrı bir proje olarak derlenip size farklı bir dll sunmamaktadır. Küçük projelerde ve tek kişilik ekiplerle yapılan projelerde mantıklı gibi gözükse de proje ve ekip büyüdükçe sıkıntılar çıkarmaya başlar. Ayrıca aşağıdaki gibi Compiler Directive’ler kullanarak, platform spesifik kodlarınızı yazabilirsiniz. Bu ilk bakışta kolay ve kullanışlı gibi gözükmesine rağmen kodlar çoğaldığında, ortalık biraz karışacaktır ve kodun okunabilirliği düşecektir.

  var path = string.Empty;
  #if WINDOWS_PHONE
  path = "windowsphone";
  #else
  
  #if __SILVERLIGHT__
  path = "silverlight";
  #else
  
  #if __ANDROID__
  path = "android";
  #else
  
  #if __IOS__
  path = "iOS";
  #else
  
  #if __TVOS__
  path = "tv";
  #else
  
  #if __WATCHOS__
  path = "watch";
  #endif
  

Gerek Unit Test yazılabilirliği olsun, gerekse daha temiz bir kod paylaşımı sağlaması vb  gibi farklı sebeplerden PCL birçok Xamarin projesinde kod paylaşımı stratejilerinin en başında gelmektedir.

Fakat bundan sonra, Xamarin.Forms 2.4.xx versiyonları ile beraber .NetStandard 2.0‘a tam destek gelmeye başladı. Microsoft’un da bundan sonra .NetCore‘a ve Standard Library ile yoluna devam edeceğini düşünürsek Xamarin projelerimizde kod paylaşımı için .NetStandard’a geçmenin vakti geldi.

netstandard

NET Standard 2.0‘da şimdiden yazılımcıların hayatını kolaylaştırmak için 20000’den fazla API, en çok kullanılan nuget paketlerinin %70’inden fazlasına uyumluluk mevcut bile. Desteğe dahil olan platformlara UWP desteği ile beraber artık Xamarin de eklendi.

.NET Standard 2.0 kütüphanesini Xamarin.iOS 10.14, Xamarin.Android 7.5, Xamarin.Mac 3.8, and Mono 5.4 versiyonları ve sonrası ile kullanabilirsiniz.

Bilgisayarınızda .Net Core ilgili eksikleriniz varsa şuradan indirebilirsiniz.

Projemde Kullanacağım .NetStandard Versiyonunu Seçerken Neleri Düşünmeliyim?

  • Ne kadar yüksek versiyon seçerseniz, o kadar çok API desteği alırsınız.
  • Ne kadar düşük versiyon seçerseniz, o kadar çok platform tarafından implement edilmiş olan versiyonu kullanıyor olursunuz.

Aslında çok da fazla düşünmenize gerek yok çünkü .Net Standard 2.0 neredeyse tüm platformlar tarafından destekleniyor.

  • NET Core 2.0
  • .NET Framework 4.6.1
  • Mono
  • Xamarin.iOS
  • Xamarin.Android
  • Xamarin.Mac
  • UWP

Tüm bu platformlar hali hazırda Net Standard 2.0′ı implement etmiş durumda.

Peki projemizdeki kod paylaşımını .Net Standard 2.0 olacak şekilde değiştirmekten bahsedeyim. Sıfırdan bir proje oluşturarak işe başlayacağım. Henüz kod paylaşımı için ilk başta Visual Studio bize seçenek olarak .Net Standard 2.0 sunmadığı için PCL seçimi yapacağım ve akabinde bunu .Net Standard 2.0 olarak değiştireceğim.

pclselect

pcl

 

Yukarıdaki seçeneklerle sıfırdan bir Xamarin.Forms projesi oluşturuyorum ve bana yandaki gibi bir Solution veriyor (Windows seçeneği için “İptal” tuşuna bastım. Şu an için onunla ilgilenmiyorum).

Projede hiçbir değişiklik yapmadan önce, solution’ı derliyorum. Tüm solution’ın sorunsuz derlendiğine emin olduktan sonra Solution’a sağ tıklayıp Add New Project diyorum.

 

Aşağıdaki gibi sol taraftaki menüden .Net Standard’ ı seçiyorum ve isimde hiçbir değişiklik yapmadan ClassLibrary1 olarak projemi solution’ a ekliyorum.

netstandartadd

Solution’a eklediğim bu SCL’min versiyonu default olarak (Eğer pc’nizde yüklü ise) 2.0 olarak gelmektedir. ClassLibrary1 projesine sağ tıklandığında en altta Properties’e tıklarsam karşıma gelen pencerenin Target framework kısmında SCL versyionunu görüp bu versiyonda değişiklik yapabilirim.

sclsel

Şimdi ClassLibrary1 projemde Dependencies’e sağ tıklayıp Manage Nuget Packages.. ‘a tıklayıp Xamarin.Forms kütüphanesini ekleyeceğim.

versxam

Yukarıda gördüğünüz gibi, ClassLibrary1 projeme Xamarin.Forms 2.4.0.282 (bu yazıyı yazarken ki son stabil versiyon) kütüphanesini ekleyip derliyorum. Projenin derlendiğine emin olduktan sonra, Pcl projesindeki App.xaml ve MainPage.xaml dosyalarını CTRL tuşuna basarak seçiyorum ve mouse yardımı ile iki dosyayı SCL projeme sürükleyip bırakıyorum (yani kopyalamış oluyorum). Bunu yaptıktan sonra artık PCL projemi solution’dan silebilirim. PCL projesini silmeden önce eğer projenizde daha fazla dosya varsa tüm dosyaları SCL’e kopyaladığınıza emin olun mutlaka.

ClassLibrary1 olarak eklediğim SCL’nin adını, bu proje mouse ile seçili iken F2 tuşuna basarak,  App1 olarak değiştiriyorum (ilk oluşturduğumda projeme verilen default isim bu olduğu için App1 yaptım. Eğer sizin önceden oluşturduğunuz bir projeniz varsa ve PCL in adı örneğin MyApp ise SCL’e de App1 değil bu  MyApp ismini veriniz.).

sclselected

Solution’ımın son hali yandaki gibidir. Gördüğünüz gibi App1 projesi artık bir .NetStandart2.0 projesidir. Dependencies altındaki Xamarin.Forms paketinin yanında sanki sorunluymuş gibi bir ünlem işareti var ama bunu şimdilik dikkate almayın. Projede sorun olmamasına rağmen Visual Studio bunu şimdilik yapıyor ancak Visual Studio’yu açıp kapattığınızda sorun düzeliyor. Fonksiyonel olarak bir sıkıntı olmadığına emin olmak için SCL projemizi derliyoruz. Projenin derlendiğine emin olduktan sonra Android ve iOS projeme bu App1 projemin referansını ekliyorum.

İlk başta gelen App1 ismindeki Portable Class Library projemi Solution’dan sildiğimde, referanslar Android ve iOS projesinde kalkmış oldu. Bu yüzden bu yeni oluşturduğum SCL kütüphanemin referansını, iOS ve Android projeme tekrar ekliyorum. Şimdi yeniden Android ve iOS projelerimi derlediğimde sorunsuz derlendiğini görüyorum. Bu noktadan itibaren Xamarin.Forms projeme .NetStandard 2.0 ile devam edebilirim.

 

Bu yazımda kısaca, var olan veya yeni oluşturduğum bir Xamarin.Forms projemin kod paylaşım stratejisi olarak nasıl PCL’den .NetStandard’a geçebileceğimizi anlatmaya çalıştım.

Özetlemek gerekirse:

  • Solution’a yeni bir .NetStandard 2.0 ClassLibrary’si ekliyorum.
  • Bu yeni eklediğim SCL’e Xamarin.Forms nuget Package’ını ekliyorum.
  • Önceden oluşan PCL’deki dosyaları (xaml, .cs vs) bu yeni oluşturduğum SCL içerisine taşıyorum.
  • PCL projesini solution’dan kaldırıyorum.
  • SCL’in ismini projeden kaldırdığım PCL’in ismi olacak şekilde değiştiriyorum.
  • Son olarak bu yeni oluşturduğum SCL’in referansını Android ve iOS projelerime ekliyorum.

 

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

 

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

Selamlar arkadaşlar. Bu yazımda .NetStandard ın ne olduğunu, çıkış amacını, .Net platformları arasında ne tür bir öneme sahip olduğunu anlatıp, birçok kişiden duyduğum “Ya şimdi bu .netcore çıktı, netstandard geldi, zaten netframework vardı, ne oluyor arkadaş, ben neyi nasıl kullanacağım?” şeklindeki kafa karışıklıklarını tamamen gidermeye çalışacağım. Ayrıca farklı .Net platformları arasındaki kod paylaşım tekniklerine değinip, .NetStandard’ın PCL(Portable Class Library)’den farkına da değineceğim.

Öncelikle şuradan başlayalım, farklı .NET platformları derken nelerden bahsediyoruz?

  • .NET Core
  • .NET Framework
  • Mono
  • Xamarin.iOS
  • Xamarin.Android
  • Xamarin.Mac
  • Universal Windows Platform
  • Windows
  • Windows Phone
  • Windows Phone Silverlight
  • Unity

Evet aslında yukarıda ki listeye baktığımıza, artık bir kişinin “Ben .net developer’ım”  demesiyle birlikte aslında ne kadar geniş çapta işler çıkartabileceğini görebiliriz 🙂 Birbirinden farklı ve güzel birçok platform mevcut. Oyun geliştirmek, iOS, Android ve WP ye native mobil uygulamalar yazmak,  Web uygulamaları geliştirmek, Windows, Xbox, Console vs bir çok ihtiyaç için birçok uygulamayı .Net kullanarak geliştirebiliyoruz. Fakat bu yelpaze genişledikçe, Visual Studio daki Solution’lar büyüdükçe, Client’ların arayüz istekleri değiştikçe, ortaya çıkan en büyük soru şu oluyor

–  Ben tüm projemde ortak olması gereken kodları nasıl paylaştıracağım?

Ortak olması gereken kodlar nelerdir biraz düşünelim;

  • Application Services katmanı olabilir. Uygulamanın tüm business ihtiyacını yazacağımız katmanı ortak kullanılacak şekilde bir kere yazmak mantıklı olabilir.
  • Ortak bir Data Acces Katmanı olabilir. Repository, UnitOfWork vs gibi pattern ler ile ihtiyacımıza göre düzenleyip yazdığımız, belki bir ORM (EntityFramework, NHibernate) veya mikro ORM (Dapper vs.) kullandığımız, ortak bir data access katmanı mantıklı olabilir.
  • Ortak bir Networking Layer olabilir. Uygulamalarımız dış dünya ile haberleşirken, API’lar ile yapacakları haberleşmeleri ayrı ayrı yazmak istemeyiz. Tek bir yerde, kurallara uygun, düşünülmüş bir katman yazmak çok daha faydalı olacaktır.
  • DTO (Data Transfer Object) – POCO (Plain Old CLR Objects) lar ortak bir yerde olabilir. Evet spesifik projeler içerisinde model şekilleri değişip, client’a özel modeller oluşturmak gerekebilir. Ama en nihayetinde DB’den çıkan entity leri Data Access den bir üst level a taşımamak mantıklı olucaktır. Bu yüzden ortak bir dto katmanı mantıklı olabilir.
  • Bunların dışında Cross-Cutting-Concerns denilen, tüm uygulamalarda kullanılacak olan, Log, Security, Cache vb. gibi fonksiyonaliteleri bir abstraction olarak ortak bir yerde yazmak mantıklı olabilir.

Peki, evet bunları tek bir yerde yazalım ama, farklı .Net platformlarında bunları nasıl kullanacağım? Kullanabilir miyim?

Şüphesiz ki sorunun cevabı evet olmalı =) Bunu nasıl başaracağımıza bakalım. .NetStandard’tan önce bunu yapabilmek için 2 popüler yöntemimiz mevcut idi.

  • Shared Asset Projects (SAP)
  • Protable Class Library (PCL)

SAP: File linking gibi çalışan, Compiler Directive’ler kullanarak, platform spesifik kodlarınızı yazabileceğimiz bir teknikti. Örneğin

 
 #if __IOS__
  path = "iOS";
  #else

şeklinde SAP projesi içerisindeki bir .cs dosyasında, sadece Xamarin.iOS için çalışmasını istediğimiz bir kod yazabilirdik. Projeler derlendiğinde, SAP ayrı bir dll olarak değil, her bir referans olduğu projenin içerisine gömülmüş olarak hayatını sürdürürdü. Fakat bu yöntem tahmin edersiniz ki ortaya çok yönetilebilir olmayan ve karmaşık kodlardan oluşan projeler çıkartırdı.

PCL: SAP projeleri ile ilerlemenin pek yönetilebilir olmaması ve kod karmaşıklığına sebep olması yüzünden PCL kullanmak daha da mantıklı oluyordu. Fakat bu durumda da karşımıza farklı sıkıntılar çıkıyordu.

Şöyle düşünelim, bir solution var. İçerisinde her katman ayrı ayrı yazılmış güzelce. Fakat önyüz olarak, Silverlight, Xamarin.iOS, Web ve Desktop olması gerekiyor. Tüm bu farklı .Net platformları kendine has, kendine özel sınıfları ve fonskiyonaliteleri barındıran farklı Base Class Library (BCL)  ler kullanıyor. Peki bunların arasında ortak kümeyi nasıl bulacağız? Ya kulllandığımız özel bir sınıf, Web, Desktop ve Sliverlight ile uyumlu ama Xamarin.iOS ile uyumlu değilse ne olacak?

İşte bu problemlerle sonradan karşılaşıp kötü sürprizler yaşamamak için, .net ekibi şöyle bir çözüm getirdi.

  • Farklı  PCL Profiles 

Ne kadar farklı .Net platformları arasında kod paylaşmak istiyorsanız, kullanabileceğiniz ortak API sayısı da ters orantılı olarak düşüyordu. Yani sadece Web ve Desktop önyüzü ile proje geliştirirken neredeyse tüm .Net Framework üne ait sınıfları kullanabilirken, işin içine Silvelight, Xamarin, WP vs girmeye başladığında birçok sınıfı kullanamaz hale geliyordunuz. Örnek bir listeye bakalım

PCL Profile PCL Platforms
Profile7 .NET Framework 4.5, Windows 8
Profile31 Windows 8.1, Windows Phone Silverlight 8.1
Profile32 Windows 8.1, Windows Phone 8.1
Profile44 .NET Framework 4.5.1, Windows 8.1
Profile49 .NET Framework 4.5, Windows Phone Silverlight 8
Profile78 .NET Framework 4.5, Windows 8, Windows Phone Silverlight 8
Profile84 Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile111 .NET Framework 4.5, Windows 8, Windows Phone 8.1
Profile151 .NET Framework 4.5.1, Windows 8.1, Windows Phone 8.1
Profile157 Windows 8.1, Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile259 .NET Framework 4.5, Windows 8, Windows Phone 8.1, Windows Phone Silverlight 8

İşin içine Xamarin, UWP vs girmeye başladıkça çarşı hepten karışmaya başlıyor, API sayısı iyice azalıyor, tüm projelerinizde kullandığınız popüler bir nuget paketini bile ortak projelerinize ekleyemez oluyordunuz, çünkü ilgili PCLProfile ında çalışmayabiliyordu.

Peki ne olacak? Belli ki bu kod paylaşım işine daha kalıcı bir çözüm lazım. Aslında gerekli olan, farklı .Net platformlarının uyması gereken bir standard olması lazım 🙂

Bunun da farkına varan Microsoft .NetStandard ı hayatımıza kattı.

Nedir Bu .NetStandard?

Aslında işin derinine inmeden, .NetStandard ı  ve 2.0 ile ilgili bazı özellikleri özetleyerek bu bölümü sonlandıralım.

  • .NetStandard’ı tüm .Net platformlarının bundan böyle implemente etmek zorunda olduğu bir Interface gibi düşünebiliriz.
  • Birçok API ı içerisinde barındıran, tüm platformların da bunları implement etmek zorunluluğu sayesinde, farklı .Net platformlarını birleştiren ve gelecekteki ayrışmaları önleyecek bir Unified BCL olarak düşünebiliriz 🙂 2.0 ile beraber ortak API sayısı 32.000 i geçti ve bir önceki versiyona oranla(1.6) %142 büyüdü. Bu çok ciddi bir oran. Aşağıda bazı Temel API ları listeliyorum.
    • Xml (Xlinq, XmlDocument, Xsd, Xsl, Xpath)
    • Serialization (DataContract, Binary Formatter, XML)
    • Networking (Http, Sockets, WebSockets, Mail)
    • IO (Files, Compression, MMF)
    • Threading (Thread, ThreadPool, Task)
    • Core (Primitives, Collections, Linq, Interop, Reflection)
  • .NetStandard 2.0 ile beraber gelen bir uyum sağlama katmanı sayesinde, netstandard kütüphanelerinizden  sayısı her geçen gün artan .NET Framework binary leri referance alabileceksiniz, Ve Tabii ki, önceki tüm .NetStandard versiyonlarına ait API larıda kullanabileceksiniz.
  • .NetStandard artık gelecekte PCL in yerini alacak, bu yüzden şuandan itibaren yeni projelerinizin tümünde kod paylaşımı olarak, .Netstandard ı tercih edebilirsiniz.

Hazır PCL projelerinden bahsetmişken ve .NetStandard ın gelecekte PCL in yerini alacağını söylemişken, yukarıdaki PCL Profile tablomuza birde Profile ların uyumlu olduğu .NetStandard versiyonlarını ekleyip gösterelim. Bu profile lar ile mevcut proje geliştirenler, PCL yerine ilgili .NetStandard kütüphanelerine geçekten çekinmesinler =)

PCL Profile .NET Standard PCL Platforms
Profile7 1.1 .NET Framework 4.5, Windows 8
Profile31 1.0 Windows 8.1, Windows Phone Silverlight 8.1
Profile32 1.2 Windows 8.1, Windows Phone 8.1
Profile44 1.2 .NET Framework 4.5.1, Windows 8.1
Profile49 1.0 .NET Framework 4.5, Windows Phone Silverlight 8
Profile78 1.0 .NET Framework 4.5, Windows 8, Windows Phone Silverlight 8
Profile84 1.0 Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile111 1.1 .NET Framework 4.5, Windows 8, Windows Phone 8.1
Profile151 1.2 .NET Framework 4.5.1, Windows 8.1, Windows Phone 8.1
Profile157 1.0 Windows 8.1, Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile259 1.0 .NET Framework 4.5, Windows 8, Windows Phone 8.1, Windows Phone Silverlight 8

Bu bölümü burada sonlandıralım. Bir sonraki bölüm de .NetStandard da ile ilgili yazmaya devam edeceğim. İşin biraz daha otaya çıkışını, biraz daha derinini öğrenmek isterseniz, bir sonraki yazımda görüşek üzere 🙂

 

Yardımcı kaynaklar:

https://github.com/dotnet/standard/blob/master/docs/faq.md

https://docs.microsoft.com/en-us/dotnet/standard/net-standard

https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard/

https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0