> ## Content Index
> Fetch the complete content index at: https://hasan-aytac.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# C# 2026: Async Streams ve Cancellation ile Akış İşleme
- URL: https://hasan-aytac.com/csharp-2026-async-streams-cancellation/
- Published: 2026-10-13T08:58:19.000Z
- Updated: 2026-10-13T08:58:19.000Z
- Description: IAsyncEnumerable, await foreach ve CancellationToken ile sayfalı verileri işleyin. .NET 10 üzerinde çalışan örneklerle iptal, hata ve kaynak yönetimini anlayın.
- Author: Hasan Aytaç
- Tags: csharp, dotnet, programming, C# 2026, #editorial-csharp-2026

> **Sürüm notu:** Örnekler 2026’da kararlı bir hedef olan **.NET 10 / C# 14** üzerinde çalışır. `IAsyncEnumerable<T>` ve `await foreach`, C# 14 yeniliği değildir; C# 8 döneminden beri kullanılan araçlardır.

Binlerce siparişi sayfa sayfa okuyan bir uygulama düşünün. Bütün sayfaları bir listeye ekleyip işlemeyi en sona bırakmak kolaydır; fakat ilk sonucu görmek için tüm sorguların bitmesini bekler ve elde edilen veriyi bellekte tutarsınız.

Async streams, tüketicinin veriler hazır oldukça çalışmasını sağlar. Doğru kullanıldığında hem kontrol akışını hem de kaynak yönetimini anlaşılır hâle getirir. Ama tek başına paralellik, sınırsız performans veya otomatik iptal garantisi vermez.

Bu yazıda dış bir servise bağlanmadan çalışan bir sipariş akışı oluşturacağız. Ardından bu akışı nasıl iptal edeceğimizi ve hataları nerede yakalamamız gerektiğini göreceğiz.

## 1\. Hangi dönüş tipini seçmelisiniz?

| İhtiyaç                                   | Yaygın tercih                            |
| ----------------------------------------- | ---------------------------------------- |
| Tek bir asenkron sonuç                    | Task<T>                                  |
| Bütün sonuçlara birlikte ihtiyaç var      | Task<List<T>> veya benzer bir koleksiyon |
| Sonuçları hazır oldukça işlemek gerekiyor | IAsyncEnumerable<T>                      |

Hiçbiri her durumda diğerinden üstün değildir. Küçük bir veri kümesinde tüm listeyi almak daha basit olabilir. Sayfalı API, uzun süren rapor veya sürekli üretilen olay akışında ise `IAsyncEnumerable<T>` anlamlı bir seçenek hâline gelir.

Buradaki ayrım, sadece dönüş tipini değiştirmek değildir. Tüketici artık “bütün iş tamamlandı mı?” yerine “bir sonraki kayıt hazır mı?” sorusunu sorar.

## 2\. Sayfaları sırayla üreten bir akış

.NET 10 konsol projesinde aşağıdaki `OrderReader.cs` dosyasını oluşturun:

```csharp
using System.Runtime.CompilerServices;

namespace CSharp2026.AsyncStreams;

public sealed record Order(int Id, decimal Total);

public static class OrderReader
{
    public static async IAsyncEnumerable<Order> ReadAsync(
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        for (int page = 1; page <= 3; page++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            var orders = await LoadPageAsync(page, cancellationToken);

            foreach (var order in orders)
            {
                cancellationToken.ThrowIfCancellationRequested();
                yield return order;
            }
        }
    }

    private static async Task<IReadOnlyList<Order>> LoadPageAsync(
        int page, CancellationToken cancellationToken)
    {
        // Ağ/veritabanı beklemesini taklit eder; dış sisteme bağlanmaz.
        await Task.Delay(10, cancellationToken);
        return [new Order(page * 10 + 1, 100m), new Order(page * 10 + 2, 200m)];
    }
}

```

Bu örnek üç sayfa üretir; her sayfada iki sipariş vardır. `Task.Delay`, ağ veya veritabanı beklemesini taklit eder. Gerçek bir dış sisteme istek yapılmaz.

Akışın önemli noktaları:

- `async` ile `yield return` birlikte kullanılır.
- Bir sayfa hazır olduğunda içindeki kayıtlar tüketiciye sırayla sunulur.
- Sonraki sayfa, mevcut sayfanın kayıtları tüketildikten sonra istenir.
- İptal hem bekleme işlemine iletilir hem de kayıtların arasında kontrol edilir.

Üretici, bütün siparişleri biriktiren ortak bir liste oluşturmaz. Yine de o anki sayfa bellektedir. Dolayısıyla “stream kullanınca hiç bellek tüketilmez” demek yanlış olur; bellek ihtiyacı sayfa boyutuna ve tüketicinin ne yaptığına bağlıdır.

## 3\. await foreach ile tüketmek

`Program.cs` içindeki ana akış:

```csharp
using CSharp2026.AsyncStreams;

```

```csharp
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
int count = 0;
decimal total = 0;

try
{
    await foreach (var order in OrderReader.ReadAsync().WithCancellation(timeout.Token))
    {
        count++;
        total += order.Total;
        Console.WriteLine($"Sipariş {order.Id}: {order.Total}");
    }
    Console.WriteLine($"İşlenen: {count}; toplam: {total}");
}
catch (OperationCanceledException) when (timeout.IsCancellationRequested)
{
    Console.WriteLine("İşlem iptal edildi veya süre doldu.");
}

```

Normal koşullarda çıktı şöyle olur:

```text
Sipariş 11: 100
Sipariş 12: 200
Sipariş 21: 100
Sipariş 22: 200
Sipariş 31: 100
Sipariş 32: 200
İşlenen: 6; toplam: 900

```

Örnekte beş saniyelik süre sınırı var. Veri üretimi çok kısa olduğu için normal çalışmada tüm kayıtlar işlenir. Gerçek bir ağ işlemi takıldığında veya kullanıcı iptal istediğinde aynı mekanizma akışın durdurulmasına yardım eder.

`await foreach` burada kayıtları **sırayla** işler. İçine `await SaveAsync(order)` koyarsanız bir siparişin kaydı tamamlanmadan bir sonrakine geçilmez. Bu, çoğu zaman istenen davranıştır. Paralellik gerekiyorsa onu ayrıca ve sınırlandırılmış biçimde tasarlamalısınız; her kayıt için kontrolsüz bir task başlatmak iyi bir çözüm değildir.

## 4\. WithCancellation neden tek başına yeterli değil?

`WithCancellation(token)`, token’ı enumerator oluşturulurken iletir. Üreticinin bu token’ı gerçekten kullanması gerekir.

Async iterator’daki `[EnumeratorCancellation]` niteliği, tüketiciden gelen token’ın iterator gövdesindeki parametreye ulaşmasını sağlar. Üretici hem parametreyle hem numaralandırma yoluyla token alıyorsa derleyici bunları uygun şekilde birleştirebilir.

Ancak bu bağlantı, iptali bütün işlemler için sihirli biçimde etkinleştirmez. Aşağıdaki adımlar hâlâ sizin sorumluluğunuzdadır:

1. Token’ı `HttpClient`, veritabanı veya diğer asenkron çağrılara iletmek.
2. Uzun CPU işlemlerinde uygun aralıklarla iptali kontrol etmek.
3. İptali genel bir hata gibi yutup devam etmemek.

Örnekte `Task.Delay(10, cancellationToken)` bu zincirin bekleme tarafını, `ThrowIfCancellationRequested()` ise kayıtlar arasındaki kontrolü gösterir.

`catch` filtresine de dikkat edin. Kendi token’ımızın iptal edildiği durumda beklenen iptal mesajını gösteriyoruz. Farklı bir işlemden gelen her `OperationCanceledException` otomatik olarak “kullanıcı iptal etti” anlamına gelmez.

## 5\. Hatalar ne zaman ortaya çıkar?

Bir async iterator metodu çağırmak, tüm veriyi hemen okumaz. Asıl iş numaralandırma başladığında yürür. Bu nedenle yalnızca `ReadAsync()` çağrısını `try` içine almak yeterli değildir; **`await foreach` döngüsünü de kapsamalısınız.**

Akış üçüncü sayfada hata verirse ilk iki sayfadaki kayıtlar zaten işlenmiş olabilir. Bu durum iş mantığınız açısından önemlidir. Rapor üretmek ile ödeme işlemi yapmak aynı hata stratejisine sahip olamaz.

Kalıcı yan etkiler varsa şu soruları baştan yanıtlayın:

- Aynı kayıt yeniden işlenirse sonuç güvenli mi?
- İşlem nereden devam edecek?
- Kısmi başarı kullanıcıya nasıl bildirilecek?
- Hangi hatalar tekrar denenebilir, hangileri kalıcıdır?

Bütün akışı baştan tekrar etmek, daha önce işlenen kayıtların etkilerini çoğaltabilir. Retry uygulamak tek başına idempotency sağlamaz. Kayıt kimliği, checkpoint veya işlemin niteliğine uygun başka bir mekanizma gerekebilir.

## 6\. Kaynak yönetimi ve üretim ortamı

`await foreach`, döngü tamamlandığında, hata oluştuğunda veya `break` ile çıkıldığında enumerator’ı asenkron biçimde dispose eder. Bu, akışı durdurmanın kaynaklar açısından önemli bir parçasıdır; yine de üretici açtığı kaynakları doğru yönetmelidir.

Bir veritabanı reader’ı veya ağ kaynağı kullanıyorsanız yaşam süresini `using` / `await using` ile uygun kapsamda tutun. Akış henüz tüketilmeden gereken kaynağı kapatmak, daha sonra ortaya çıkan hatalara yol açabilir.

Örnekteki sabit üç sayfa yalnızca öğretim amaçlıdır. Gerçek bir serviste `hasNextPage`, cursor veya boş sayfa gibi bitiş koşullarını uygulayın. Tekrarlayan cursor’lara, tutarlı sıralamaya, zaman aşımına ve gerekirse toplam sayfa sınırına dikkat edin.

Son olarak tüketici bütün sonuçları yeniden bir listeye toplarsa, akışın bellek avantajı büyük ölçüde kaybolabilir. Aracı değil, uçtan uca veri işleme biçimini değerlendirin.

## Sonuç: akış, iptal ve yan etkileri birlikte tasarlayın

Async streams’in en güçlü yanı, veriler hazır oldukça işlemeyi açık bir programlama modeliyle sunmasıdır. Sağlam bir çözüm için buna iptal zinciri, kaynak yaşam süresi ve kısmi başarı stratejisi de eklenmelidir.

[Üç makalenin kod örneklerini indir — ZIP](https://hasan-aytac.com/content/files/2026/10/csharp-2026-code-samples.zip). `AsyncStreams` projesi kayıt sayısını, toplamı, sıralamayı, ilk kayıttan sonraki iptali ve önceden iptal edilmiş token davranışını altı doğrulamayla sınar.

## Kaynaklar ve devamı

- [Microsoft Learn — Async stream üretmek ve tüketmek](https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/generate-consume-asynchronous-stream?ref=hasan-aytac.com)
- [Microsoft Learn — IAsyncEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1?ref=hasan-aytac.com)
- [Microsoft Learn — EnumeratorCancellationAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.enumeratorcancellationattribute?ref=hasan-aytac.com)
- Serinin ilk yazısı: [C# 14 Extension Members](https://hasan-aytac.com/csharp-2026-extension-members/).
- Serinin ikinci yazısı: [field ve null-conditional assignment](https://hasan-aytac.com/csharp-2026-field-null-conditional-assignment/).

*Bu yazı yapay zekâ desteğiyle hazırlanmıştır. İndirilebilir örnekler .NET SDK 10.0.401 üzerinde, C# 14 ayarı ve uyarıları hata kabul eden yapılandırmayla derlenip çalıştırılmıştır.*