.NET Core 3.0 ile Gelen Yenilikler ve VS2019 Preview / What’s Coming With .NET Core 3.0

Merhaba Yazılım Meraklıları,

Bu yazıda .NET Core 3.0 ile gelen yenilikleri inceleyeceğiz ve genel bir inceleme yapacağız. Aynı zamanda C# 8.0 (preview) ile gelen yeniliklere ve kullanımlara da bakıyor olacağız.

Hemen hemen tüm örneklemeler MS Windows OS üzerinden anlatılmıştır.

Gerekli kurulumlar ile Başlayalım

– Öncelikle bu özelikleri kullanabilmek için Visual Studio 2019 ‘a (yazı tarihiyle preview version) ihtiyacınız olacak.

Visual studio 2019 download

VisualStudio2019 Preview Installing

Şimdilik sadece aşağıdaki workload’ları yükleyeceğim.

 

– Ardından linkten .Net Core 3.0 ‘ı indirip kurun.
.Net Core 3.0 SDK Installing

– Son olarak hazır kurulumlara ısınmışken şuradan .Net Framework 4.8 (early access) de indirip kurun.

Artık latest version IDE, Framework ve SDK’lara sahibiz. Bu yazının tarihi itibariyle tabi ki = )
O halde bizim için yeni oynayacak hangi oyuncakları koymuşlar incelemeye geçebiliriz.

WPF (Windows Presentation Foundation), Windows Forms -> Windows Desktop

.Net core 3.0 ile en çok göze çarpan yenilik WinDesktop tarafında gözüküyor.
Artık WPF ve WinForms applicationlarını .Net Core bu sürümü ile destekliyor. Bu da haliyle en major feature yapıyor bunu.

VS2019 üzerinden birer WPF ve WinForm application oluşturalım.

  • WPF Application

Tüm adımları aşağıdaki ekran görüntülerinde görebilirsiniz.
VS2019CreateProjectScreen

Yukarıdaki adımlarda sırasıyla Project Type’ımızı seçtik > ProjectName ve Location folder gibi bilgileri set edip > Basit bir text change ile Execute ettik.

Bu işlemleri komut satırı ile de yapabilirsiniz. Onun da kodu aşağıdadır.

dotnet new wpf -o wpfDotNetCore3Dot0Exm
  • Windows Form

Tüm adımları aşağıdaki ekran görüntülerinde görebilirsiniz.

Yukarıdaki adımlarda sırasıyla Project Type’ımızı seçtik > ProjectName ve Location folder gibi bilgileri set edip > Basit bir text change ile Execute ettik.

Bu işlemleri aşağıdaki komut satırı ile de yapabilirsiniz.

dotnet new winforms -o wfDotNetCore3Dot0Exm

ASP.Net Core Web Applications

  • Razor Componentler – UI.
  • gRPC Apps
  • Web Applicationlarımız için ekstra bir efor sarfetmemize gerek kalmadan Local’de HTTPS desteği veriyor.

ve daha saymadığım bir çok bir çok ince detay var. Derinlemesine girip bu makalede saatlerinizi almamak için başlıklar üzerinden hoşuma gidenlere değiniyorum.

Örneğin Razor Componentler’e ufaktan girebiliriz.
.Net Developer olarak UI’da çok efektif işler çıkarabileceğiniz bir template olmuş. DI tarafı dikkatimi çekti, UI’a Classlarınızı inject edebiliyorsunuz. Basit bir uygulama ile inceleyelim.

Create Razor Component Application

Yukarıdaki ekran görüntüsündeki sağ tarafta olan “Advanced” bölümünün altında “Configure for HTTPS” i seçip daha sonra CREATE yapın.

Razor Components Application’ımız oluştu hemen F5 ile çalıştırırsak aşağıdaki gibi HTTPS ile geldiğini göreceğiz.

Not: İlk defa çalıştırınlar, HTTPS için bir uyarı penceresi alacaklar. Onu onaylayıp devam edin.

Biraz code tarafını inceleyelim.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .AddNewtonsoftJson();

            services.AddRazorComponents();

            services.AddSingleton<WeatherForecastService>();
        }

Eğer bu projede “WeatherForecastService” ini incelersek. Startup.cs içerisinde yukarıda gibi “WeatherForecastService” i Singleton olarak set edilmiş ve aşağıdaki kod parçacağına baktığınızda ise “UI” olan FetchData.razor içerisinde nasıl inject edilip, nasıl kullanıldığını görüyorsunuz.

@page "/fetchdata"
@using WARazorComponents.Services
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@functions {
    WeatherForecast[] forecasts;

    protected override async Task OnInitAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Ayrıca örnek olarak oluşturduğumuz razor component web application’ın içerisindeki diğer page lerdeki kod yazımlarına da bakarsanız sanırım buna JavaScript duyarlıklı C# diyebiliriz. =)

.Net Core 3.0 ile Yürütülebilir Uygulamalar (Executable Applications)

Herhangi bir Tool kullanmadan direkt Command Prompt üzerinden uygulama oluşturup execute edebileceğiniz code lineları aşağıda paylaşıyorum. Hem Window için hem de Linux için.

Windows için / For Windows :

/*Komutları sırasıyla teker teker çalıştırın!*/
dotnet new console -o consoleDotNetCore3Dot0
cd consoleDotNetCore3Dot0
dotnet build
cd binDebugnetcoreapp3.0
dir /b     /*Bunu kullanmadan direkt aşağıdaki satırı da çalıştırabilirsiniz*/
consoleDotNetCore3Dot0.exe

Linux için / For Linux :

/*Komutları sırasıyla çalıştırın*/
root@123456789:/consoleDotNetCore3Dot0# dotnet build
root@123456789:/consoleDotNetCore3Dot0# cd bin/Debug/netcoreapp3.0/
root@123456789:/consoleDotNetCore3Dot0/bin/Debug/netcoreapp3.0# ls  /*Bunu çalıştırmaya bilirsiniz, görmeniz içindir.*/
root@123456789:/consoleDotNetCore3Dot0/bin/Debug/netcoreapp3.0# dotnet consoleDotNetCore3Dot0.dll

Ranges & Index

Belki de en çok hoşuma giden yeniliklerden birisi bu oldu.
Basitçe tanımlayacak olur ise bir dizi için tanımlayacağımız startIndex ve endIndex değişkenleriyle, ilgili dizi içerisinde bu indexlerimizin aralığında kalan itemları alabilmemizi sağlıyor.
endIndex’i için “^” prefixi kullanılıyor ve index aralığını belirtmek için ise “..” kullanılmaktadır.
Direkt aşağıdaki kod parçağını çalıştırıp inceleyebilirsiniz.

static void Main(string[] args)
{
    Index startIndex = 1;
    Index endIndex = ^3;
    string[] charSet = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
    var indexRange = charSet[startIndex..endIndex]; //"b".."w"
    var indexRangeResult = String.Join(",", indexRange.Select(s => s));
    Console.WriteLine(indexRangeResult);
    Console.ReadKey();
}

Async Streams

IEnumerable<> interface inin async kullanımı için “IAsyncEnumerable” bulunmaktadır. Yani Task<> kullanmadan async bir method oluşturabiliyoruz.  Bu oluşturduğumuz methodun listesini de “foreach” döngüsünün başına “await” getirerek kullanabiliyoruz. Aşağıda api aracılığıyla döviz verilerini getiren örnek kod parçaları üzerine uyguladım.

public async IAsyncEnumerable<string> DovizDataAsync()
{
    await foreach (var item in GetAsyncDovizData())
        yield return string.Join(", ", item.name, item.code, item.sellPrice);
}

public async IAsyncEnumerable<DovizDto> GetAsyncDovizData()
{
    var response = new WebClient().DownloadString("https://api.canlidoviz.com/web/items?marketId=1&type=0");
    var deObject = JsonConvert.DeserializeObject<List<DovizDto>>(response);
    foreach (var item in deObject)
        yield return item;
}


//Data Transfer Object
 public class DovizDto
{
    public string name { get; set; }
    public string code { get; set; }
    public float todayLowestBuyPrice { get; set; }
    public float todayHighestBuyPrice { get; set; }
    public float todayLowestSellPrice { get; set; }
    public float todayHighestSellPrice { get; set; }
    public float yesterdayClosingBuyPrice { get; set; }
    public float yesterdayClosingSellPrice { get; set; }
    public float buyPrice { get; set; }
    public float sellPrice { get; set; }
    public float dailyChange { get; set; }
    public float dailyChangePercentage { get; set; }
    public string lastUpdateDate { get; set; }
}

 

Son olarak herşeyi toparlarsak ve değinmediğimiz diğer özellikleri de ekleyecek olursak.

  • Visual Studio’ya farklı bir hava kazandırılmış. Beğenilir veya beğenilmez göreceğiz.
  • WinDesktop tarafına WPF ve WinForm Applicationlar getirildi. Aynı zamanda EF de destekleniyor.
  • Razor Componentler ile Server-Side & Client-Side mantığı artık bir .Net developer için aynı diyebiliriz.
  • IOT desteği
  • Linux üzerinde TLS 1.3 ve OpenSSL 1.1.1 destekleniyor
  • WinClientOS: Win7 ve üzeri için destekleniyor.
  • WinServerOS: 2012 R2 SP1 ve üzeri için destekleniyor.
  • .csproj içerisine <NullableContextOptions>enable</NullableContextOptions>” tagini ekleyerek null check kullanımı rahatlığından faydalanabilirsiniz.
  • Cryptography (Buna farklı bir yazıda değinebiliriz.)
  • JsonReader (Buna da farklı bir yazıda benchmark yaparak bakacağız. Linkini buraya koyarım.)
  • C# 8 özellikleri .Net Framework 4.8 ile kullanılabiliyor.

Bir başka yenilikte görüşmek üzere.

.Net Core ile Protobuf Kullanımı ve JSON Performans Karşılaştırması

Merhabalar,

Öncelikle eğer kemerlerinizi bağlamadıysanız hemen bağlayın, çünkü birazdan Protobuf-net ile uçuşa geçeceğiz. Nasıl mı?

Şöyle ki kodlara girmeden önce kısaca Protocol Buffers’dan bahsedelim.

Google tarafından geliştirilmiştir ve amacı ise yapılandırdığınız dataların serileştirilmesini(serialization) sağlamaktır.

Örnek olarak herkes tarafından bilinen XML Format’ını düşünebilirsiniz. Bu örnekteki handikap şudur. XML DOM’ları içerisinde gezinmek kompleksdir ve Serialize/Deserialize işlemlerinin de maliyeti biraz yüksektir. Buna alternatif olarak yine tüm geliştiriciler tarafından bilinen ve sektörde kabul görmüş JSON Format karşımıza çıkıyor. Bunların detaylarına girmeyeceğim. Çünkü asıl konumuzdan uzaklaşmak istemiyorum. Protobuf bize bu serileştirme işlemlerini birçok formata göre çok daha hızlı bir şekilde gerçekleştiriyor.

Şimdi, Protobuf’ın nasıl kullanıldığına ve JSON’a göre nasıl bir performans gösterdiğine bakacağımız uygulamamıza geçelim.

  • İlk olarak bir .Net Core Console Application oluşturun.
  • Ardından Package Manager Console’u açıp şu komutları çalıştırın.
 Install-Package protobuf-net
Install-Package Newtonsoft.json



 

Protobuf Contract Tanımlamaları

Contract based çalışan bir yapısı var ve ilgili model classlarımıza attribute olarak tek seferlik tanımlayıp kullanıyoruz.  Örnek için kendime şu contractları oluşturdum. Siz isterseniz farklı bir senaryo üretebilirsiniz.

using ProtoBuf;
using System;

namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    [ProtoContract]
    [ProtoInclude(3, typeof(Order))]
    public class BaseContract
    {
        [ProtoMember(1)]
        public int CreatedBy { get; set; }
        [ProtoMember(2)]
        public DateTime CreatedDate { get; set; }
    }
}
namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    public enum AddressType
    {
        Billing = 1,
        Delivery = 2
    }
}
using ProtoBuf;

namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    [ProtoContract]
    public class Address
    {
        [ProtoMember(1)]
        public string City { get; set; }
        [ProtoMember(2)]
        public string Line1 { get; set; }
        [ProtoMember(3)]
        public string Line2 { get; set; }
        [ProtoMember(4)]
        public AddressType Type { get; set; }
    }
}
using ProtoBuf;
using System.Collections.Generic;

namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    [ProtoContract]
    public class Customer
    {
        [ProtoMember(1)]
        public string FullName { get; set; }
        [ProtoMember(2)]
        public string Phone { get; set; }
        [ProtoMember(3)]
        public List<Address> Addresses { get; set; }
    }
}
using ProtoBuf;

namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    [ProtoContract]
    public class Product
    {
        [ProtoMember(1)]
        public string ProductSKU { get; set; }
        [ProtoMember(2)]
        public string ProductName { get; set; }
        [ProtoMember(3)]
        public int Quantity { get; set; }
        [ProtoMember(4)]
        public decimal Price { get; set; }
    }
}
using ProtoBuf;
using System;
using System.Collections.Generic;

namespace ProtobufnetVSNewtonsoftjson.Contracts
{
    [ProtoContract]
    public class Order : BaseContract
    {
        [ProtoMember(1)]
        public string OrderNumber { get; set; }
        [ProtoMember(2)]
        public DateTime OrderDate { get; set; }
        [ProtoMember(3)]
        public decimal TotalPrice { get; set; }
        [ProtoMember(4)]
        public List<Product> Products { get; set; }
        [ProtoMember(5)]
        public Customer CustomerInfo { get; set; }
    }
}

Yukarıdaki Contract tanımlamalarında inheritance ve diğer caseler için kullanımlarını görebilirsiniz. Simple bir yapıya sahip. Her member Unique’dir.
Örnekte “ProtoContract, ProtoInclude ve ProtoMember(N)” attribute’leri kullanılmıştır.

ProtoInclude: Inheritance kullanılacaksa bu etiket eklenir.

ProtoContract: Object modelinin en tepesine eklenen etiketdir.

ProtoMember(N): Object modelindeki memberlara uygulanır ve N(=1,=2=3, vb.) değeri unique olmalıdır. Aksi halde runtimeda hata fırlatır.

  • Oluşturduğumuz contractlar ile program.cs->main method içerisine inMemory data ekliyoruz.
        #region inMemoryData
            var data = new Order
            {
                CreatedBy = 1,
                CreatedDate = DateTime.Now,
                OrderDate = DateTime.Now,
                OrderNumber = "MSTF123456789",
                TotalPrice = 150,
                Products = new List<Product>{
                                              new Product {
                                                            ProductName = "Test Product 1" ,
                                                            ProductSKU = "TP123456",
                                                            Quantity = 2,
                                                            Price = 25
                                                          },
                                              new Product {
                                                            ProductName = "Test Product 2" ,
                                                            ProductSKU = "TP123457",
                                                            Quantity = 1,
                                                            Price = 100
                                                          }
                                            },
                CustomerInfo = new Customer
                {
                    FullName = "Mustafa KOÇ",
                    Phone = "5551112233",
                    Addresses = new List<Address> {
                                                   new Address {
                                                                   City ="İstanbul",
                                                                   Line1 = "Maslak Mh.",
                                                                   Line2 = "Şişli",
                                                                   Type = AddressType.Delivery
                                                                },
                                                   new Address {
                                                                   City ="İstanbul",
                                                                   Line1 = "Maslak Mh.",
                                                                   Line2 = "Şişli",
                                                                   Type = AddressType.Billing
                                                               }
                                                  }
                }
            };
        #endregion
  • Hemen ardına aşağıdaki kod bloğunu ekleyin.
                byte[] serializedData;
                Order deserializedOrder = new Order();
    
                #region Protobuf-net
                var watchProtoBuf = System.Diagnostics.Stopwatch.StartNew();
                for (int i = 0; i < 1000000; i++)
                {
                    using (var mStream = new MemoryStream())
                    {
                        ProtoBuf.Serializer.Serialize(mStream, data);
                        serializedData = mStream.ToArray();
                    }
                    using (var mStream = new MemoryStream(serializedData))
                    {
                        deserializedOrder = ProtoBuf.Serializer.Deserialize<Order>(mStream);
                    }
                }
                watchProtoBuf.Stop();
                var elapsedMsProtoBuf = watchProtoBuf.ElapsedMilliseconds;
                Console.WriteLine($"ProtoBuf-net 1.000.000 row data Serialize/Deserialize ExecutionTime(ms): {elapsedMsProtoBuf}");
                #endregion
    
                #region  Newtonsoft.Json
                string jsonSerializedData = string.Empty;
                var watchJson = System.Diagnostics.Stopwatch.StartNew();
                for (int i = 0; i < 1000000; i++)
                {
                    jsonSerializedData = JsonConvert.SerializeObject(data);
                    deserializedOrder = JsonConvert.DeserializeObject<Order>(jsonSerializedData);
                }
                watchJson.Stop();
                var elapsedMsJson = watchJson.ElapsedMilliseconds;
                Console.WriteLine($"Newtonsoft.Json 1.000.000 row data Serialize/Deserialize ExecutionTime(ms): {elapsedMsJson}");
                #endregion

    Yukarıdaki kod bloğunda “Protobuf-net” c# dili ile kullanımı #region etiketleri içerisinde belirtilmiştir. Hemen altında ise Json kullanım örneği yine #region etiketleri arasında gösterilmiştir.

    ProtoBuf.Serializer.Serialize(mStream, data) satırında inMemory datasını stream ediyoruz ve sonrasında byte dizisine dönüştürüyoruz. Bu dönüşüp sonucu bize serialized datayı veriyor. Datayı deserialize etmek için ise serialized datayı stream edip ProtoBuf.Serializer.Deserialize<T>(mStream) methodunu kullanıyoruz. Burada T generic dönüşümün yapılacağı tür, mStream ise deserialize edilecek data.

Şimdi çalıştırıp performans analizini gözlemleyebiliriz.

Senaryo: Serialize/Deserialize for 1.000.000 object
Formatlar: Protobuf, Json

Result:

ProtoBuf-net 1.000.000 row data Serialize/Deserialize ExecutionTime(ms): 9715
Newtonsoft.Json 1.000.000 row data Serialize/Deserialize ExecutionTime(ms): 25840

Resultımızı görselleştirirsek performans farkını daha iyi görebiliriz.

 

Application Open Source Code: https://github.com/mustafa-koc/ProtobufnetVSNewtonsoftjson