Async Await Best Practices Cheat Sheet

Summary of Asynchronous Programming Guidelines

Name Description Exceptions
Avoid async void Prefer async Task methods over async void methods Event handlers
Async all the way Don’t mix blocking and async code Console main method
Configure context Use ConfigureAwait(false) when you can Methods that require con­text

The Async Way of Doing Things

To Do This … Instead of This … Use This
Retrieve the result of a background task Task.Wait or Task.Result await
Wait for any task to complete Task.WaitAny await Task.WhenAny
Retrieve the results of multiple tasks Task.WaitAll await Task.WhenAll
Wait a period of time Thread.Sleep await Task.Delay

Know Your Tools

There’s a lot to learn about async and await, and it’s natural to get a little disoriented. Here’s a quick reference of solutions to common problems.

Solutions to Common Async Problems

Problem Solution
Create a task to execute code Task.Run or TaskFactory.StartNew (not the Task constructor or Task.Start)
Create a task wrapper for an operation or event TaskFactory.FromAsync or TaskCompletionSource<T>
Support cancellation CancellationTokenSource and CancellationToken
Report progress IProgress<T> and Progress<T>
Handle streams of data TPL Dataflow or Reactive Extensions
Synchronize access to a shared resource SemaphoreSlim
Asynchronously initialize a resource AsyncLazy<T>
Async-ready producer/consumer structures TPL Dataflow or AsyncCollection<T>

Async and Await Guidelines

There are many new await-friendly techniques that should be used instead of the old blocking techniques. If you have any of these Old examples in your new async code, you’re Doing It Wrong(TM):

Old New Description
task.Wait await task Wait/await for a task to complete
task.Result await task Get the result of a completed task
Task.WaitAny await Task.WhenAny Wait/await for one of a collection of tasks to complete
Task.WaitAll await Task.WhenAll Wait/await for every one of a collection of tasks to complete
Thread.Sleep await Task.Delay Wait/await for a period of time
Taskconstructor Task.Run or TaskFactory.StartNew Create a code-based task

 

Yazılım Mühendisliği Ünvanları

Yazılım mühendisliği ünvanları firmadan firmaya değişen bir yapı göstermektedirler. Bana göre bir yazılım firmasında minimumda olması gereken ünvan basamakları aşağıdaki şekilde olabilir.

    1. Junior Software Engineer
    2. Software Engineer
    3. Senior Software Engineer
    4. Lead Software Engineer
    5. Principal Software Engineer

1. Junior Software Engineer:

  • Mesleğin ilk basamağıdır.
  • Sıradan işler için bile diğer yazılım mühendislerinden yardım alması gerekir.

2. Software Engineer:

  • Üst seviye görevlerin, iyi tanımlanmış daha küçük görevlere bölünmesine ihtiyaç duyar.
  • Yardım almadan çalışabilir.

3. Senior Software Engineer:

  • Üst seviye görevleri, iyi tanımlanmış daha küçük görevlere bölebilir.
  • Bir projeyi baştan sona kendi başına tamamlayabilir.
  • Bir çok problemi kendi başına çözebilir.

4. Lead Software Engineer:

  • Senior Software Engineer yeteneklerine sahiptir.
  • Küçük takımları yönetebilir. (3-7 kişilik)
  • Diğer yazılım mühendislerine, yeteneklerini arttırmaları için, öğretir ve akıl hocalığı yapar.
  • Diğer yazılım mühendislerinin daha üretken olmasını sağlar.

5. Principal Software Engineer:

  • Lead Software Engineer yeteneklerine sahiptir.
  • Çok kapsamlı başarıları vardır veya şirket için olmazsa olmaz bir anahtar pozisyondadır.

WSDL Değişiklikleri

Uyumlu Değişiklikler:

  • Yeni bir WSDL operasyon tanımı ve ilgili mesaj tanımları ekleme.
  • Yeni bir WSDL bağlantı noktası türü tanımı ve ilgili operasyon tanımları ekleme.
  • Yeni bir WSDL bağlayıcı ve servis tanımları ekleme.
  • Bir mesaj tanımına, yeni bir isteğe bağlı XML şeması elemanı veya nitelik tanımı ekleme.
  • Bir mesaj tanımı tipinin veya bir XML şema elemanının kural ayrıntı düzeyinin azaltılması.
  • Bir mesaj tanımı tipine yeni bir XML şeması jokeri ekleme.
  • Yeni bir isteğe bağlı WS-Policy belirteci ekleme.
  • Yeni bir WS-Policy alternatifi ekleme.

Uyumsuz Değişiklikler:

  • Mevcut bir WSDL operasyon tanımını yeniden adlandırma.
  • Mevcut bir WSDL operasyon tanımını kaldırma.
  • Mevcut bir WSDL operasyon tanımının MEP’ini değiştirme.
  • Mevcut WSDL operasyon tanımına bir hata mesajı ekleme.
  • Bir mesaj tanımına yeni bir gerekli XML şema öğesi veya nitelik beyanı ekleme.
  • Bir mesaj tanımının bir XML şema elemanının kısıt ayrıntı artan veya nitelik beyan.
  • Bir mesaj tanımının isteğe bağlı veya gerekli XML şema öğesi veya niteliği yeniden adlandırma.
  • Bir mesaj tanımından isteğe bağlı veya gerekli XML şema öğesi veya niteliği veya joker kaldırma.
  • Yeni gerekli WS-Policy savı ya da ifadesini ekleme.
  • Yeni göz ardı edilebilir WS-Policy ifadesini ekleme. (çoğu zaman)

?. ve ?[ Operatörleri

Visual Studio 2015 ile gelen (?.) ve (?[) operatörleri ile üyeye erişim (?.) veya index operasyonu (?[) öncesinde null testi yapabilmekteyiz.

int? length = customers?.Length; // customers null ise null değilse customers.Length
Customer first = customers?[0];  // customers null ise null değilse customers[0]
int? count = customers?[0]?.Orders?.Count();  // customers, customers[0], customers[0].Orders null ise null değilse customers[0].Orders.Count()

ASP.NET MVC Web API ile Dosya Sunucusu

Örnek projeyi buradan indirebilirsiniz.

  • Asp.Net Web Api projesi oluşturulur.
  • Dosyalar App_Data klasörü altına atılır.
  • Web.Config’de  system.webServer altına aşağıdaki satır eklenir.
    <modules runAllManagedModulesForAllRequests="true"/>
  • Controllers altında FileController.cs oluşturulur.
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Web;
    using System.Web.Http;
    
    namespace FileServer.Controllers
    {
        public class FilesController : ApiController
        {
            // GET api/files/test_v1.jpg?sessionId=test
            public HttpResponseMessage GetFile(string sessionId, string name)
            {
                HttpResponseMessage response;
    
                // Session kontrolü burada yapılabilir.
                if (sessionId == "test")
                {
                    var filePath = HttpContext.Current.Server.MapPath("~/App_Data/" + name);
    
                    if (File.Exists(filePath))
                    {
                        response = new HttpResponseMessage 
                                       { 
                                           StatusCode = HttpStatusCode.OK, 
                                           Content = new StreamContent(File.OpenRead(filePath)) 
                                       };
    
                        response.Content.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(Path.GetExtension(filePath)));
    
                        return response;
                    }
                    else
                    {
                        response = new HttpResponseMessage(HttpStatusCode.NotFound);
                    }
                }
                else
                {
                    response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                }
    
                return response;
            }
        }
    }
    

Programlar Arası İletişim

Windows işletim sistemi, uygulamalar arasında iletişimi ve veri paylaşımını sağlamak için çeşitli mekanizmalara sahiptir. Bu mekanizmalara işlemler arası iletişim – Interprocess Communications (IPC) adı verilir. Tipik olarak uygulamalar IPC’yi sunucu ve istemci olarak kategorize ederek kullanırlar.

Windows’ta aşağıdaki IPC mekanizmaları mevcuttur:

  • Pano (Clipboard)
  • Bileşen Nesne Modeli (COM)
  • Veri Kopyalama (Data Copy)
  • Dinamik Veri Takası (DDE)
  • Dosya Adresleme (File Mapping)
  • İleti Yuvaları (Mailslots)
  • Borular (Pipes)
  • Uzak Yordam Çağrısı (RPC)
  • Windows Soketleri (Windows Sockets)

Ben bunların arasından otomatik veri dönüştürme ve farklı işletim sistemleri ile çalışabilme özelliğinden dolayı RPC’yi tercih ediyorum. RPC ile yüksek performanslı dağıtık uygulamalar geliştirilebilinir. RPC kütüphanesi olarak da Zyan Communication Framework‘ü tercih ediyorum.

Örnek bir sohbet uygulaması oluşturdum. Host çalıştığı anda bağlantıya başlıyor ve 3 adet farklı client çalıştırıyor. Client’lar birbirlerine mesaj gönderebiliyor.

Buradan oluşturduğum örnek projeyi indirebilirsiniz.

Vergi Kimlik Numarası Kontrolü

Aşağıdaki metot ile vergi kimlik numaralarını(VKN) kontrol edebiliriz.

bool IsVknValid(string vkn)
{
    try
    {
        if (vkn.Length == 10)
        {
            var x = new int[9];
            var y = new int[9];

            for (int i = 0; i < 9; i++)
            {
                x[i] = (int.Parse(vkn[i].ToString()) + 9 - i) % 10;

                y[i] = (x[i] * (int)Math.Pow(2, 9 - i)) % 9;

                if (x[i] != 0 && y[i] == 0)
                {
                    y[i] = 9;
                }
            }

            return ((10 - (y.Sum() % 10)) % 10) == int.Parse(vkn[9].ToString());
        }
        else
        {
            return false;
        }
    }
    catch (Exception)
    {
        return false;
    }
}

Regex Replace

Xml dönüşümleri için regex kullanabiliriz. Aşağıdaki örnekte Regex.Replace metodu ile old.xml, new.xml’e dönüştürülmektedir. Her “Row” etiketine “E” özelliği eklenmektedir.

old.xml

<Rows>
  <Row A="73" B="2" C="0" D="3" />
  <Row A="140" B="3" C="1" D="2" />
  <Row A="129" B="2" C="2" D="2" />
  <Row A="255" B="4" C="3" D="10" />
  <Row A="334" B="2" C="4" D="48" />
</Rows>

new.xml

<Rows>
  <Row A="73" B="2" C="0" E="0" D="3" />
  <Row A="140" B="3" C="1" E="0" D="2" />
  <Row A="129" B="2" C="2" E="0" D="2" />
  <Row A="255" B="4" C="3" E="0" D="10" />
  <Row A="334" B="2" C="4" E="0" D="48" />
</Rows>

Program.cs

using System;
using System.IO;
using System.Text.RegularExpressions;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var oldXml = File.ReadAllText("old.xml");

            var newXml = Transform(oldXml);

            File.WriteAllText("new.xml", newXml);

            Console.WriteLine("Done.");

            Console.ReadKey();
        }

        private static string Transform(string xml)
        {
            var newXml = Regex.Replace(xml,
                                      "C=\"(?<td>.*?)\"",
                                      (m) =>
                                      {
                                          var td = m.Groups["td"].Value.Trim();

                                          return "C=\"" + td + "\" E=\"0\"";
                                      });

            return newXml;
        }
    }
}

Custom Attribute Oluşturma

TC kimlik numarası ve cep telefonu için alan doğrulama amaçlı yazdığım attribute’ler aşağıdadır.

Tckn.cs

using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace WpfValidation.CustomAttibutes
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    sealed public class Tckn : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            bool result = false;

            try
            {
                if (value.ToString().Length == 11)
                {
                    long tcNo = Int64.Parse(value.ToString());

                    long atcno = tcNo / 100;
                    long btcno = tcNo / 100;

                    long c1 = atcno % 10; atcno = atcno / 10;
                    long c2 = atcno % 10; atcno = atcno / 10;
                    long c3 = atcno % 10; atcno = atcno / 10;
                    long c4 = atcno % 10; atcno = atcno / 10;
                    long c5 = atcno % 10; atcno = atcno / 10;
                    long c6 = atcno % 10; atcno = atcno / 10;
                    long c7 = atcno % 10; atcno = atcno / 10;
                    long c8 = atcno % 10; atcno = atcno / 10;
                    long c9 = atcno % 10; atcno = atcno / 10;
                    long q1 = ((10 - ((((c1 + c3 + c5 + c7 + c9) * 3) + (c2 + c4 + c6 + c8)) % 10)) % 10);
                    long q2 = ((10 - (((((c2 + c4 + c6 + c8) + q1) * 3) + (c1 + c3 + c5 + c7 + c9)) % 10)) % 10);

                    result = ((btcno * 100) + (q1 * 10) + q2 == tcNo);
                }

                return result;
            }
            catch (Exception)
            {
                return false;
            }   
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
        }
    }
}

MobilePhone.cs

using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Text.RegularExpressions;

namespace WpfValidation.CustomAttributes
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    sealed public class MobilePhone : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            try
            {
                var number = value.ToString();

                return Regex.IsMatch(value.ToString(), "^[0-9]{1" + (number.StartsWith("0") ? "1" : "0") + "}$");
            }
            catch (Exception)
            {
                return false;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
        }
    }
}

SHA-2 Hesaplama

SHA-2(Secure Hash Algorithm) ABD Ulusal Güvenlik Ajansı tarafından tasarlanan kriptografik hash fonksiyonları (SHA-224, SHA-256, SHA-384, SHA-512) kümesidir. Bir hash fonksiyonu rastgele dizilimli bir veri elemanı kümesinden, metin dosyası gibi, sabit uzunluklu bir değer hesaplayan algoritmadır. Bulunan bu değer daha sonra kopyalanan orjinal verinin bütünlüğünü kontrol etmek için kullanılabilir. MD5 algoritmasının güvenlik konusunda bazı sıkıntıları olduğundan yeni uygulamalarda SHA-256 ve SHA-512 kullanılması tavsiye edilmektedir. Aşağıdaki kod ile 256 veya 512 bit uzunluğunda hash oluşturabiliriz.

static string GetSha(string text, int bitLength)
{
    switch (bitLength)
    {
        case 256:
            return BitConverter.ToString(new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "").ToLower();
        case 512:
            return BitConverter.ToString(new SHA512Managed().ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "").ToLower();
        default:
            return null;
    } 
}