Erhan Ballıeker

Asp.Net Core 2.0’a Giriş – Bölüm 4 (Startup.cs, Configure, Middleware hakkında)

Selamlar, Asp.Net Core 2.0 Giriş seri postlarından bu bölümde Startup.cs deki Configure metodundan ve Middleware yapısından bahsedeğim. Middleware yapısı çok Asp.Net Core için çok önemli bir değişiklik ve bunun detaylarına ilerleyen postlarda gireceğiz.

Startup.cs deki Configure metodunun yaptığı iş şudur; Asp.Net Core projenizdeki Http Request pipeline nını konfigüre etmek. Peki ne demek bu Pipeline ı konfigüre etmek bunu aşağıdaki resimle bir inceleyelim. Klasik Asp.Net projelerinde Request in izlediği yol çok uzun ve karmaşıkça idi.Sizin belkide hiç kullanmayacağınız birçok yapıdan request geçiyor ve sizin elinize o şekilde ulaşıyordu. Ama aşağıdaki resimde göreceğiniz gibi, Asp.Net Core pipeline nına siz ne eklerseniz middleware olarak sadece o yapılardan geçecek request. Hiç bir middleware eklenmediğinde hiç birşey olmayacak. Bir önceki örneğimizdeki tek bir middleware eklenirse sadece o middleware den geçecek.

pipeline

Asp.Net Core projelerinde resimde görebileceğiniz gibi, koca bir MVC framework pipeline ı bile ayrıca projeye middleware olarak ekleniyor eğer istenirse. Size kalmış, MVC framework ün imkanlarından faydalanacak olanlar pipeline a bunu ekleyebilir, Bunun öncesinde kendi yazdıkları yada built-in gelen middleware lerden Authentication middleware ini koyup, static file ları serve etmek için (css,javascript,images vs..) Static Files Middleware ini pipeline a ekleyebilirler.

HttpRequest, eklediğiniz sırada middleware lerden geçerek, son middleware den sonra aynı şeklide geri dönerek tüm middleware lerden geçip browser a ulaşır. Bu requestin middleware lerdeki akışı sırasında WebServer feature larına ulaşma imkanı veriyor Asp.Net Core uygulamamız. Bir de daha büyük resimde request in browserdan uygulamamıza gelip tekrar browser a geri döndüğü akışa bakalımpipeh.Bu resmi adım adım izleyecek olursak, sırası ile şunlar oluyor.

  • Request browserdan External Web Server a (bu resimde IIS kullanıldığı varsayıldı, ama bu Nginx, Apache olabilir.) ulaşır.
  • External Web Server (IIS) dotnet runtime çalıştırır.
  • dotnet runtime, CLR ı load edip, sizin Application nınızdaki entry point (Program.cs deki Main() matodu ) i arar ve çalıştırır.
  • dotnet runtime Application ı çalıştırdıktan sonra Internal Web Server olan Kestrel ayağa kalkar.(Kestrel olmak zorunda değil, ama internal bir web server için çok çok iyi bir seçenek çünkü hem cross platform bir web server, hem performansı çok iyi).
  • Main metodunuz çalıştıktan sonra, WebHostBuilder ilgili ayarlar ile ayağa kalkar, Startup dosyası ile beraber uygulamanız konfigüre edildikten sonra, HttpRequest, IIS den Kestret’e yönlendirilir.
  • Startup dosyasındaki eklenen middleware lerinizden geçen request tekrar Kestrel e ulaşır, Kestrel,  External Web Server a response u iletir ve sonunda response browser a gelir.

Bu eski System.Web yaklaşımından çok daha verimli ve çok daha resource friendly dir. Klasik Asp.Net projelerinin Windows bağımlı olma sebebi zaten System.Web idi. System.Web assemly IIS e bağlı, IIS de, Windows a bağlıydı. Bu yüzden asp.Net projelerimiz windows dışında işletim sistemlerinde çalışamıyordu.

Şunu da belirtmekte fayda var. External bir web server a mutlaka ihtiyacınız yok aslında. Ama Kestrel çok performanslı ve cross platform bir web server olsada, IIS,Nginx, Apache gibi tam donanımlı web serverlar kadar yetenekli değil, bu yüzden zorunlu olmamakla beraber yüksek ihtimalle bir external web server kullanıyorsunuz. 🙂

Peki özet teorik bilgiden sonra uygulamamıza geri dönelim. ikinci bir middleware ekleyip, requestin oradan geçişlerini loglayarak, middleware yapısının çalışma şeklini iyice anlamaya çalışalım.

Startup.cs dosyasında ki Configure metodunda aşağıdaki değişiklikleri yapalım.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger logger)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            app.Use(async (context, next) =>
            {
                logger.LogInformation("First middleware working BEFORE second middleware");
                var serviceProvider = context.RequestServices;
                var userService = serviceProvider.GetRequiredService();
                var users = await userService.GetUsers();
                await context.Response.WriteAsync($"Hello World! - UserCount: {users.Count}");
                await next();
                logger.LogInformation("First middleware working AFTER second middleware");
            });

            app.Run(async (context) =>
            {
                logger.LogInformation("second middleware working");
                await context.Response.WriteAsync("Second middleware done!");
            });
        }

Kod tarafında yaptığımız değişiklikler şunlar;

  1. Configure metoduna ILogger<Startup> objesini inject ediyoruz. Bu daha Default WebHostBuilder konfigüre edilirken oluşturulup uygulamamıza bizim için register edilmiş oluyor Asp.Net Core tarafından
  2. İlk middleware deki app.Run metodunu app.Use olarak değiştiriyoruz. Çünkü Run metodu gelen request i shortcut yapıp sonraki middleware lere iletmeden geri döner. Use metodunda ise, context dışında ikinci bir parametre olarak RequestDelegate objesi gelir(bir sonraki middleware delegesi olarak). buna next  adını verdik, tüm işlemlerimiz bittikten sonra bu delegate i await ediyoruz. Tam bu noktada ikinci middeware miz çalışmaya başlar.
  3. Logları koyduğumuz yerlere dikkat ediniz. Biliyoruz ki middleware ler, uygulamaya eklendiği sırada request leri karşılar, tam tersi sırada da response ları karşılarlar. Projeyi çalıştırdığımızda logların nasıl bir sırayla basılacağına bakalım.

Bu sefer External web server kullanmadan sadece Kestrel i yani Internal Web Server ı kullanarak bu işlemi yapalım. Bunun için Visual Studio da, Run butonun yanındaki oku tıklayıp, IISExpress yerine projenin adının olduğu seçeneği seçmemiz yeterli (örneğimizde MeetupAspNetCore). Bu IIS Express i değil doğrudan Kestrel üzerinden requestleri karşılamamızı ve browser a dönmemizi sağlayacak. Uygulamayı çalıştırıp çıktımıza bakalım.

mware

resimde de göreceğiniz gibi, önce birinci middleware çalıştı, await next() dediğimiz anca ikinci middleware in logu basıldı, ikinci middleware de işini tamamladıkan sonra response geri dönerken,birinci middleware de await next(), dediğimiz yerden sonraki kod bloğunda yazılan log çalışmış oldu.

Bu yazımızı da burada tamamlayabiliriz. Middleware lerden kısaca bahsetmiş olduk, bir AspNet Core projesine gelen request in tüm akışını inceledik ve middleware lerin çalışma şeklini örnekledik.

Asp.Net Core 2.0 yazı serimizde artık temel noktaları gördük diyebiliriz.  Ama henüz Introduction ı tamamen yaptık dememiz için çok şey var 🙂

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

Asp.Net Core 2.0’a Giriş – Bölüm 3 (Startup.cs, Built-In Dependency Injection hakkında)

Selamlar, Asp.Net Core serimize kaldığımız yerden devam ediyoruz. Önceki iki yazımızda, projemizi oluşturduk, solution daki tüm dosyaları inceledik, projenin nasıl ayağa kalktığına değinmeye çalıştık. WebHostBuilder objemizin Run() metoduyla artık Web application a dönüşen projemiz, tam olarak hayatına başlamadan önce son ve önemli ayarlamalarını yapacağımız kısım Startup.cs dosyası olacak. Buna başka bir isim de verebilirdik tabii isme çok takılmayın, ama default gelen ve genelde göreceğiniz ismi ile Startup.cs, DI container ını ve Middleware leri ayarlayacağımız önemli bir dosya olduğunu bilelim 🙂

Serinin başında oluşturduğumuz ve halen boş haliyle duran projemizde ki bu dosyanın içeriğine baktığımızda aşağıdaki gibi 2 temel metod görüyoruz.

    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

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

            app.UseStaticFiles();

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }
    }

Şimdi bu iki metodu detaylıca inceleyelim.

Configure Services

Bu metod .Net Core ile birlikte gelen, built-in Dependency Injection Container ının ayarlarını yapacağımız kısım olacak. DI klasik Asp.net tarafında optional iken, artık bilmemiz gereken bir durum haline geliyor. Çok kısaca özetlemek gerekirse Dependency Injection için şunu diyebiliriz:

“Uygulama içerisinde ben senden bu (ICriticalService) tipte bir şey istediğimde bana şu (MainCriticalService) tipi ver, bir de şu (MainCriticalService) tipin ne kadar süre hayatta kalacağını ben sana en başında söyleyeyim gerisini sen yönet.”

Birde aşağıdaki resim üzerinden bakalım. Dependency Injection Pattern i, genelde IoC denen (Inversion Of Control) Containerları kullanılarak yapılır. Container’ı uygulama start olduktan sonra ilk iş olarak initialize edilir, ilgili service registration larını  yapar uygulama içerisinde bu tipler çağırıldığı zaman gerçek sınıfları Container ın bize verilmesi beklenir. Bu yazının temel amacı DI ı anlatmak olmadığından fazlaca buraya takılmadan Asp.Net Core tarafında bu işler nasıl dönüyor, Farklı Lifetime tipleri neler onlara bakalım.

(Sadece DI konusu tek başına çok derin, en son şunu yazmak isterim güzel bir örnektir.Dependency Injection 5 yaşındaki bir çocuğa nasıl anlatılır? Cevap şuna benzer: Eğer 5 yaşındaki çocuk acıktığı zaman, doğrudan buzdolabına gidip istediği şeyi almaya kalkarsa, yanlış şeyi alabilir, şeklinden, görüntüsünden farklı şey zannedip yanlış şeyleri alabilir, tarihi geçmiş bir içeceği-yiyeceği alabilir, ortalığı batırabilir 🙂 Bunun için aslında yapması gereken, acıktığında istediği şeyi ailesine söylemesi, ailesinin de onun istediği şeyi ona vermesidir. Burada tahmin edeceğiniz üzere çocuk yazılan app, aile IoC Container dır.)

diimg

Resimde gördüğümüz gibi bir taraftan genel olarak Interface i yolluyoruz, çağırdığımızda diğer taraftan bize isteiğimiz sınıf geliyor. En başında da bu istediğimiz söylüyoruz tabii. Asp.Net Core tarafında 3 tipte lifetime option var. bunlar

  • Transient:  Bu yaşam tipi ile bir objeyi register ettiğimiz zaman, Container dan bu obje her istendiğinde yenisi verilir. Her defasında dolaptan yeni bir içecek almak gibi düşünelim.
  • Scoped: Bu yaşam tipi ile bir objeyi register ettiğimizde, Container bize ilgili Request sonlana kadar aynı objeyi verir, yeni bir request geldiğinde yeni bir obje oluşturulur. Tek bir öğünde 5 yaşında ki çocuğumuz aynı şey isteyip durursa, dolaptan yenisini almak yerine önünde yarım kalmışı ona geri itelediğimizi düşünebiliriz 🙂
  • Singleton: Bu yaşam tipi ile bir obje register ettiğimizde ise, Container dan obje istenildiğinde sadece ilk sefer için oluşturulur, sonrasında da her zaman aynı obje verilir. Dolap örneğine dönersek, çocuğumuz her susadığında ve bizden su istediğinde ona hep aynı bardaktan vermek gibi düşünebiliriz (Bu biraz zorlama oldu sanki 🙂 )

Lifetime ların önemi büyük, özellikle birbirini çağıran metodlar olduğunu düşünürsek (ki bu durum bir uygulama için gayet normal bir durum), singleton register edilmiş bir metot içerisinde scoped bir servis çağırdığımızda çarşı karışacaktır, yaşam döngüleri birbirini etkileyemeye başlayacaktır. Bunlarla ilgili daha derinlemesine detayları başka bir yazıda değineceğim.

Peki bu durum bize ne sağladı?

  1. Objeler arasındaki sıkı bağlar ortadan kalkmış oldu. Dolayısı ile Unit Testing de kolaylaşmış oldu
  2. Objelerin lifetime larını yönetme zorluluğu ortadan kalmış oldu (Ama registraion lara, ve birlikte kullanımlara dikkat ederek :))

Şimdi kod tarafına girelim. Boş olan projemize, bir NetStandard kütüphanesi ekleyelim, ve oraya uygulamanın kullanacağı modelleri ve uygulama servislerini (Application Service) lerimizi koyalım. Örneğimiz basit olsun, Sitemizin kullanıcılarını listeleyelim ve onların sitemizdeki rollerini görelim. O halde Solution a bir adet NetStandard2.0 projesi ekliyorum ve içini aşağıdaki gibi dolduruyorum.

stndrdadded

Solution ın son hali yukarıdaki resimde göründüğü gibi oldu. Senaryonun son derece basit, User ve Role ler var. bir de bunları bize dönen Application Servicelerimiz var. kodlar şu şekilde;

   public class Users
    {
        public Users()
        {
            UserRoles = new List();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public IEnumerable UserRoles { get; set; }
    }

    public class Roles
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }

    public interface IUserService
    {
        Task GetUsers();
        Task GetRoles();
    }

   public class UserServiceInMemory : IUserService
    {
        public UserServiceInMemory()
        {
            RoleList = new List {
                new Roles { Id = 1, Name = "Admin", Description = "Admin" },
                new Roles { Id = 2, Name = "Editor", Description = "Content Editor" } };

            UserList = new List {
                new Users { Age = 31, Id = 1, Name = "Erhan Ballıeker", UserRoles = RoleList},
                new Users { Age = 30, Id = 2, Name = "Meltem Ballıeker", UserRoles = RoleList.Take(1) } };
        }

        public List UserList { get; set; }
        public List RoleList { get; set; }

        public Task<List<Roles>> GetRoles()
        {
            return Task.Run(() => RoleList);
        }

        public Task<List<Users>> GetUsers()
        {
            return Task.Run(() => UserList);
        }
    }

Peki hızlıca özetleyelim. 2 adet basit class var User ve Role class ları. bir interface miz var. Gerçek hayat senaryosunda bu UserInterface inin neler yapmasını istiyorsak ilgili metodları buraya koyuyoruz. Sonra sadece test amaçlı kullanacağımız bu interface den türeyen In-Memory çalışacak bir UserServiceInMemory class ı oluşturuyoruz. Şimdi gelelim bunu Asp.Net Core tarafındaki Container a anlatmaya.

Startup.cs dosyasındaki ConfigureServices metodunun bu Container ı konfigüre etmek amaçlı olduğunu söylemiştik. Şimdi registration işlemimizi yapalım. Core projesine NetStandard projesini referans olarak gösterdikten sonra ConfigureServices metoduna aşağıdaki kodu ekliyoruz.

services.AddSingleton<IUserService, UserServiceInMemory>();

Burada dediğimizşey şu: Ben ne zaman senden bir IUserService istersem, bana UserServiceInMemory getir, ve bu service singleton (yani uygulama boyunca 1 kere oluşturulsun ve sonra hep aynı obje kullanılsın) olsun.

Peki bu register ettiğmiz service i nasıl kullanacağız? bu blog post içerisinde Configure metoduna ve dolayısı ile Middleware yapısına çok girmeyeceğim, fakat şu şekilde test edelim. Configure metodu içerisindeki app.Run(…)  metodu ile uygulamamıza tek bir middleware eklenmiş. Yani request uygulamamıza geliyor, response a “Hello World!” yazıp geri dönüyoruz. Çok işe yarar bir uygulama değil gibi 🙂 Şimdilik, daha projemize hiçbir Controller vs eklememişken, bu service’i , bu tek middleware içerisinde test edelim. Asp.Net Core projelerinde, register edilen service’leri çağırmanın birkaç yolu var. Bunlardan biri request Context i içerisinden bu service’i almak. Bunun da detayına sonra gireceğim, ama şimdilik sadece görmüş olalım ve service imizi web uygulamamız içerisinde kullanmış olalım amacı ile, tek middleware i aşağıdaki gibi düzenleyebiliriz.

app.Run(async (context) =>
{
     var serviceProvider = context.RequestServices;
     var userService = serviceProvider.GetRequiredService<IUserService>();
     var users = await userService.GetUsers();
     await context.Response.WriteAsync($"Hello World! - UserCount: {users.Count}");
   });
}

Uygulamamızı çalıştırdığımızda browserdaki çıktının aşağıdaki gibi olduğunu görüyoruz.

cnt

Evet! servisimiz Container dan istediğimiz gibi bize ulaştı, ilgili metodu çağırıp eklediğimiz userları service den aldık. Ekrana da Count u basmış olduk. Bu yazıda çok şey yapık aslında, WebHostBuilder oluştuktan sonra, Startup dosyamızı kullan diyerek, önce ConfigureServices metodu ile DI a biraz değinip, Asp.Net Core projesi içerisinde nasıl konfigüre ediyoruz onu görmüş olduk, ve tek bir middleware üzerinde register ettiğimiz servisimizi alıp kullanabildik.

Bir sonraki yazımda, yine startup dosyasındaki ikinci bir önemli metod olan Configure ile beraber middleware yapısına değineceğim.

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.

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.

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