ASP.NET Core eğitimi kapsamında, HTTP isteğinden ayrılan arka plan işlerini bounded Channel, scoped bağımlılıklar, idempotency ve ölçüm araçlarıyla üretimde güvenli biçimde çalıştırmayı inceliyoruz.
ASP.NET Core Eğitimi: BackgroundService ile Dayanıklı İş Kuyrukları
ASP.NET Core eğitimi için bounded Channel ile kuyruk sınırı koymak
Bir HTTP endpoint'inin e-posta, PDF üretimi veya harici API senkronizasyonu gibi işi doğrudan yapması, isteğin bağlantı ömrünü iş süresine bağlar. Bir asp.net core eğitimi içinde bu ayrımı göstermek için process-içi kuyrukta System.Threading.Channels kullanın. BoundedChannelFullMode.Wait, üreticiyi yavaşlatarak bellek büyümesini sınırlar; UnboundedChannel ise ani yükte her öğeyi heap üzerinde tutar ve GC basıncını isteğe bağlı olarak sınırsız artırır.
public sealed record InvoiceJob(Guid InvoiceId, string IdempotencyKey);
public interface IInvoiceQueue
{
ValueTask EnqueueAsync(InvoiceJob job, CancellationToken ct);
IAsyncEnumerable<InvoiceJob> ReadAllAsync(CancellationToken ct);
}
public sealed class InvoiceQueue : IInvoiceQueue
{
private readonly Channel<InvoiceJob> _channel =
Channel.CreateBounded<InvoiceJob>(new BoundedChannelOptions(500)
{
FullMode = BoundedChannelFullMode.Wait,
SingleWriter = false,
SingleReader = false
});
public ValueTask EnqueueAsync(InvoiceJob job, CancellationToken ct) =>
_channel.Writer.WriteAsync(job, ct);
public IAsyncEnumerable<InvoiceJob> ReadAllAsync(CancellationToken ct) =>
_channel.Reader.ReadAllAsync(ct);
}
builder.Services.AddSingleton<IInvoiceQueue, InvoiceQueue>();
builder.Services.AddHostedService<InvoiceWorker>();Endpoint tarafında istemcinin iptal token'ını doğrudan kuyruğa geçirmek ince bir karardır: kullanıcı bağlantıyı kapattığında işin mutlaka kabul edilmesi gerekiyorsa RequestAborted yerine uygulama kapanış token'ı kullanılmalıdır. Tersine, kullanıcı iptal ettiğinde iş anlamsız hale geliyorsa RequestAborted doğrudur. Kuyruk dolu olduğunda bekleme süresini sonsuza bırakmayın; CancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(2)) ile 503 dönmek, thread-pool üzerinde uzun bekleyen HTTP isteklerini biriktirmekten daha kontrollüdür.
app.MapPost("/invoices/{id:guid}/send", async (
Guid id, IInvoiceQueue queue, CancellationToken requestAborted) =>
{
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(requestAborted);
timeout.CancelAfter(TimeSpan.FromSeconds(2));
try
{
await queue.EnqueueAsync(new InvoiceJob(id, Guid.NewGuid().ToString("N")), timeout.Token);
return Results.Accepted($"/invoices/{id}");
}
catch (OperationCanceledException) when (!requestAborted.IsCancellationRequested)
{
return Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
});.NET Core eğitimi: Worker yaşam döngüsü, scope ve hata sınıflandırması
Bir .net core eğitimi veya .net core kursu örneğinde en sık görülen hata, singleton yaşam ömürlü BackgroundService içine doğrudan scoped DbContext enjekte etmektir. Worker her iş için IServiceScopeFactory.CreateAsyncScope() çağırmalıdır; aksi halde aynı change tracker binlerce işin entity referanslarını tutar ve paralel kullanımda DbContext thread-safe olmadığı için hata üretir.
public sealed class InvoiceWorker(
IInvoiceQueue queue,
IServiceScopeFactory scopes,
ILogger<InvoiceWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var job in queue.ReadAllAsync(stoppingToken))
{
try
{
await using var scope = scopes.CreateAsyncScope();
var handler = scope.ServiceProvider.GetRequiredService<InvoiceHandler>();
await handler.HandleAsync(job, stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (TransientPaymentException ex)
{
logger.LogWarning(ex, "Geçici hata: {InvoiceId}", job.InvoiceId);
// Kalıcı kuyrukta görünürlük süresiyle yeniden deneme planlanmalıdır.
}
catch (Exception ex)
{
logger.LogError(ex, "Poison job: {InvoiceId}", job.InvoiceId);
// Hata kaydı veya dead-letter hedefi zorunludur.
}
}
}
}BackgroundService.StopAsync varsayılan olarak host kapanışında token iptali alır; ancak process sonlandırması veya container'ın kısa termination grace period'u işin ortasında kesilmesine neden olabilir. Bu nedenle işleyiciyi tekrar çalıştırılabilir tasarlayın. Uygulama içi Channel kalıcı değildir: pod yeniden başladığında henüz okunmamış kayıtlar kaybolur. Teslim garantisi gerekiyorsa Azure Service Bus, RabbitMQ veya SQL tabanlı kalıcı bir iş tablosu kullanın; process-içi kuyruk yalnızca kısa süreli yük yumuşatma için uygundur.
Bir csharp eğitimi ya da csharp kursu içeriğinde retry'nin her hataya uygulanmadığını özellikle test edin. HTTP 400, doğrulama hatası ve benzersizlik ihlali yeniden denemede düzelmez; bunları dead-letter'a yazın. HTTP 429 ve geçici ağ hatalarında ise üstel bekleme için Polly kullanın, fakat gecikme süresine jitter ekleyin. Aynı anda bin işin sabit 1 saniye sonra denemesi, bağımlı servise ikinci bir yük darbesi gönderir.
Entity Framework eğitimi: Idempotency kaydını veritabanında atomik tutmak
At-least-once teslim yapan bir broker veya kapanış sırasında yarıda kesilen worker, aynı işi birden fazla kez çalıştırabilir. Bu nedenle entity framework eğitimi örneğinde idempotency kontrolünü önce bellekte değil veritabanında benzersiz indeksle kurun. 'Önce SELECT, sonra INSERT' yaklaşımı iki worker aynı anda SELECT yaptığında yarışa açıktır; benzersiz indeks yarışın hakemidir.
public sealed class ProcessedJob
{
public long Id { get; set; }
public required string Key { get; set; }
public DateTimeOffset ProcessedAt { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProcessedJob>()
.HasIndex(x => x.Key)
.IsUnique();
}İşin yan etkisi ile idempotency kaydını aynı yerel transaction'da saklayabiliyorsanız, önce kaydı ekleyip SaveChangesAsync çağırın ve provider'a özgü unique-violation hatasını yakalayın. PostgreSQL için SQLSTATE 23505, SQL Server için hata numaraları 2601 ve 2627 ayrıştırılabilir. DbUpdateException sınıfının tamamını 'zaten işlenmiş' saymak disk, bağlantı veya constraint hatalarını gizler.
public async Task HandleAsync(InvoiceJob job, CancellationToken ct)
{
_db.ProcessedJobs.Add(new ProcessedJob
{
Key = job.IdempotencyKey,
ProcessedAt = DateTimeOffset.UtcNow
});
try
{
await _db.SaveChangesAsync(ct);
}
catch (DbUpdateException ex) when (IsUniqueViolation(ex))
{
return; // Aynı anahtar daha önce başarıyla sahiplenilmiş.
}
await _paymentGateway.SendInvoiceAsync(job.InvoiceId, ct);
}Buradaki kritik edge case şudur: benzersiz kaydı yazdıktan sonra harici ödeme çağrısı başarısız olursa iş tekrar denendiğinde kayıt nedeniyle çağrı atlanır. Harici yan etkiyi aynı veritabanı transaction'ına alamıyorsanız durum makinesi kullanın: Pending, InProgress, Completed, Failed. Harici sağlayıcı destekliyorsa aynı idempotency anahtarını HTTP header olarak da gönderin. Bu ayrıntı, c# eğitimi materyallerinde sıkça atlanan 'exactly-once' varsayımını ortadan kaldırır.
C# kursu için kuyruk kapasitesini profiling ile boyutlandırmak
Kuyruk kapasitesini 500 gibi sabit bir sayı olarak kabul etmeyin. Yük testi için k6 ile endpoint'e sabit oranlı trafik gönderin, aynı anda dotnet-counters monitor --process-id <pid> System.Runtime Microsoft.AspNetCore.Hosting çalıştırın. Önce unbounded kuyrukla 10 dakika test edin, sonra bounded kuyruk ve 2 saniyelik enqueue zaman aşımıyla aynı senaryoyu tekrarlayın. Karşılaştırmada HTTP p95/p99 gecikmesi, gc-heap-size, alloc-rate, threadpool-queue-length ve 503 sayısını aynı zaman penceresinde kaydedin.
# Sabit varış hızında 10 dakikalık k6 testi
k6 run --duration 10m --vus 50 queue-load.js
# Çalışan uygulamada sayaçları izle
dotnet-counters monitor -p 12345 System.Runtime Microsoft.AspNetCore.Hosting
# Gecikme yolu ve allocation çağrılarını örnekle
dotnet-trace collect -p 12345 --profile cpu-samplingBefore-after yorumunda yalnızca ortalama gecikmeye bakmayın. Unbounded yapı, tüketici yetişemediğinde kabul oranını korurken heap'i büyütebilir; bounded yapı ise kuyruk dolduğunda açıkça 503 üretir. Bu nedenle hedef, 503'ü sıfırlamak değil, kapasite aşımında p99 gecikmesini ve heap eğimini sınırlamaktır. dotnet-trace çıktısını PerfView veya Visual Studio Performance Profiler ile açıp Channel<InvoiceJob>.WriteAsync beklemelerinin ve JSON deserialize allocation'larının çağrı ağacındaki payını inceleyin.
Tüketici eşzamanlılığını CPU sayısına göre rastgele artırmak da hatalıdır. Harici API I/O ağırlıklıysa SemaphoreSlim ile örneğin 8 paralel iş deneyin; ardından 4, 8 ve 16 değerlerini ayrı k6 koşularında ölçün. Veri tabanı connection pool'u 100 iken 200 worker başlatmak, worker'ları connection bekletir ve HTTP istekleriyle aynı havuzu tüketir. Bu ölçüm disiplini, bir c# kursu içinde 'async daha hızlıdır' gibi mekanizmasız bir çıkarımdan çok daha değerlidir.
Blazor eğitimi: İş durumunu polling yerine güvenli şekilde yayınlamak
Bir blazor eğitimi uygulamasında kullanıcıya 'iş kabul edildi' yanıtından sonra durum göstermek için iş tablosundan GET /jobs/{key} polling yapılabilir. 2 saniyelik polling, 5.000 açık tarayıcıda saniyede 2.500 ek sorgu üretir. Durum değişimi seyrekse SignalR hub üzerinden yalnızca değişen işi yayınlamak daha uygun olabilir; yine de istemci yeniden bağlandığında kaçırdığı mesajı telafi etmek için ilk bağlantıda REST durum sorgusu zorunludur.
public sealed class JobHub : Hub { }
// Worker, işlem tamamlandığında çağırır:
await _hubContext.Clients.Group(job.IdempotencyKey)
.SendAsync("jobChanged", new { state = "Completed" }, ct);
// Blazor bileşeninde bağlantı sonrası grup üyeliği ve başlangıç durumu alınır:
await connection.StartAsync();
await connection.InvokeAsync("JoinJob", jobKey);
state = await Http.GetFromJsonAsync<JobState>($"/jobs/{jobKey}");Hub grubuna katılımı istemcinin gönderdiği anahtara körü körüne bağlamayın. JoinJob içinde kullanıcının job sahibini veritabanından doğrulayın; aksi halde tahmin edilebilir anahtarla başka müşterinin fatura durumunu dinlemek mümkündür. Bu yetkilendirme kontrolü, microsoft teknolojileri eğitimi kapsamında SignalR'ın transport ayrıntısından daha önemlidir. Çoklu instance dağıtımında SignalR scale-out için Azure SignalR Service veya desteklenen bir backplane yapılandırın; yalnızca process belleğindeki grup listesi instance'lar arasında paylaşılmaz.
İlgili Eğitim
YTÜSEM İlgili Eğitim
.NET Core ReactJS FullStack Eğitimi (Yıldız Teknik Üniversitesi SEM)
Sık Sorulan Sorular
.net core kursu projelerinde BackgroundService içine DbContext enjekte edilir mi?
Doğrudan enjekte etmeyin. BackgroundService singleton olarak yaşar, DbContext ise scoped olmalıdır. IServiceScopeFactory.CreateAsyncScope() ile her mesaj için scope oluşturun; böylece change tracker birikmez ve aynı DbContext paralel worker'lar tarafından kullanılmaz.
csharp eğitimi için Channel mı RabbitMQ mu seçilmeli?
Channel, aynı process içindeki kısa süreli tamponlama için uygundur ve uygulama kapanınca bekleyen mesajları kaybeder. Mesajın yeniden başlatma sonrası korunması, dead-letter, gecikmeli retry veya birden fazla consumer instance gerekiyorsa RabbitMQ, Azure Service Bus ya da kalıcı SQL kuyruk tablosu kullanın.
entity framework eğitimi sırasında idempotency nasıl test edilir?
Aynı IdempotencyKey ile iki HandleAsync çağrısını eşzamanlı başlatın ve veritabanında unique index tanımlı olduğundan emin olun. Test, yalnızca bir ProcessedJob satırı oluştuğunu ve harici gateway mock'unun en fazla bir kez çağrıldığını doğrulamalıdır. InMemory provider yerine benzersiz indeks davranışını gerçekten uygulayan SQL Server veya PostgreSQL test container kullanın.
c# eğitimi ve blazor eğitimi projelerinde iş ilerlemesi nasıl gösterilir?
İş durumunu kalıcı bir tabloda saklayın, SignalR ile değişiklik bildirimi gönderin ve istemci bağlanınca REST üzerinden son durumu yeniden okuyun. Hub grup üyeliğinde job sahibini doğrulayın; yalnızca jobKey bilgisine dayanarak gruba katılım vermeyin.
AI / LLM Discovery
Bu makale Opendart Akademi Microsoft / C# eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


