ASP.NET Temel En İyi Yöntemleri

Yayınlayan Mike Rousos

Bu makalede, ASP.NET Core uygulamalarının performansını ve güvenilirliğini en üst düzeye çıkarmak için yönergeler sağlanır.

Agresif bir şekilde önbelleğe alma

Önbelleğe Alma bu makalenin çeşitli bölümlerinde ele alınmıştı. Daha fazla bilgi için bkz . ASP.NET Core'da önbelleğe almaya genel bakış.

Sık erişimli kod yollarını anlama

Bu makalede sık erişimli kod yolu, sık çağrılan ve yürütme süresinin büyük bir kısmının gerçekleştiği bir kod yolu olarak tanımlanır. Sık erişimli kod yolları genellikle uygulama ölçeğini genişletmeyi ve performansı sınırlar ve bu makalenin çeşitli bölümlerinde ele alınıyor.

Aramaları engellemekten kaçının

ASP.NET Core uygulamaları, birçok isteği aynı anda işlenecek şekilde tasarlanmalıdır. Zaman uyumsuz API'ler, küçük bir iş parçacığı havuzunun çağrıları engellemeyi beklemeyerek binlerce eşzamanlı isteği işlemesine olanak sağlar. İş parçacığı, uzun süre çalışan bir zaman uyumlu görevin tamamlanmasını beklemek yerine başka bir istekte çalışabilir.

ASP.NET Core uygulamalarında sık karşılaşılan bir performans sorunu, zaman uyumsuz olabilecek çağrıları engellemektir. Birçok zaman uyumlu engelleme çağrısı, İş Parçacığı Havuzu aç kalmasına ve yanıt sürelerinin düşmesine yol açar.

veya Task<TResult>.Resultöğesini çağırarak Task.Wait zaman uyumsuz yürütmeyi engellemeyin. Ortak kod yollarında kilitleri almayın. ASP.NET Core uygulamaları, kod paralel olarak çalıştırılacak şekilde tasarlandığında en iyi performansı gösterir. AramayınTask.Run ve hemen beklemeyin. ASP.NET Core, uygulama kodunu normal İş Parçacığı Havuzu iş parçacıklarında zaten çalıştırdığından, çağrının Task.Run yalnızca gereksiz İş Parçacığı Havuzu zamanlaması ile sonuçlanması gerekir. Zamanlanan kod bir iş parçacığını engellese bile bunu Task.Run engellemez.

  • Sık erişimli kod yollarını zaman uyumsuz hale getirin.
  • Zaman uyumsuz bir API varsa veri erişimini, G/Ç'yi ve uzun süre çalışan işlem API'lerini zaman uyumsuz olarak çağırabilirsiniz.
  • Zaman uyumlu API'yi zaman uyumsuz yapmak için kullanmayınTask.Run.
  • Denetleyici/Razor Sayfa eylemlerini zaman uyumsuz yapın. Zaman uyumsuz/await desenlerinden yararlanmak için çağrı yığınının tamamı zaman uyumsuzdur.

PerfView gibi bir profil oluşturucu, İş Parçacığı Havuzuna sık sık eklenen iş parçacıklarını bulmak için kullanılabilir. Olay, Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start iş parçacığı havuzuna eklenen bir iş parçacığını gösterir.

Birden çok küçük sayfada büyük koleksiyonlar döndürme

Bir web sayfasının büyük miktarda veriyi aynı anda yüklememesi gerekir. Bir nesne koleksiyonu döndürürken, bunun performans sorunlarına neden olup olmadığını göz önünde bulundurun. Tasarımın aşağıdaki kötü sonuçları üretip üretemediğini belirleyin:

  • OutOfMemoryException veya yüksek bellek tüketimi
  • İş parçacığı havuzu açlığı (üzerinde IAsyncEnumerable<T>aşağıdaki açıklamalara bakın)
  • Yavaş yanıt süreleri
  • Sık çöp toplama

Önceki senaryoları azaltmak için sayfalandırma ekleyin. Geliştiriciler sayfa boyutu ve sayfa dizini parametrelerini kullanarak kısmi sonuç döndürme tasarımını desteklemelidir. Kapsamlı bir sonuç gerektiğinde, sunucu kaynaklarını kilitlememek için zaman uyumsuz olarak sonuç toplu işlemlerini doldurmak için sayfalandırma kullanılmalıdır.

Sayfalama ve döndürülen kayıt sayısını sınırlama hakkında daha fazla bilgi için bkz:

İade veya IEnumerable<T>IAsyncEnumerable<T>

Bir eylemden döndürülerek IEnumerable<T> seri hale getirici tarafından zaman uyumlu koleksiyon yinelemesi elde edilir. Sonuç, çağrıların engellenmesi ve iş parçacığı havuzu açlığı olasılığıdır. Zaman uyumlu numaralandırmayı önlemek için numaralandırılabilir öğesini döndürmeden önce kullanın ToListAsync .

ASP.NET Core 3.0 ile başlayarak, IAsyncEnumerable<T> zaman uyumsuz olarak numaralandıranlara alternatif IEnumerable<T> olarak kullanılabilir. Daha fazla bilgi için bkz . Denetleyici eylemi dönüş türleri.

Büyük nesne ayırmalarını en aza indirme

.NET Core çöp toplayıcısı, ASP.NET Core uygulamalarında belleğin otomatik olarak ayrılmasını ve serbest bırakılmasını yönetir. Otomatik çöp toplama genellikle geliştiricilerin belleğin nasıl veya ne zaman boşaltıldığı konusunda endişelenmesi gerekmeyecek anlamına gelir. Ancak başvurulmayan nesnelerin temizlenmesi CPU süresini aldığından geliştiricilerin sık erişimli kod yollarında nesneleri ayırmayı en aza indirmesi gerekir. Atık toplama özellikle büyük nesnelerde pahalıdır (>= 85.000 bayt). Büyük nesneler büyük nesne yığınında depolanır ve temizlemek için tam (2. nesil) bir çöp toplama gerektirir. 0. nesil ve 1. nesil koleksiyonlardan farklı olarak 2. nesil koleksiyonlar, uygulama yürütmenin geçici olarak askıya alınmasını gerektirir. Büyük nesnelerin sık sık ayrılması ve ayırmayı kaldırması tutarsız performansa neden olabilir.

Öneriler:

Önceki gibi bellek sorunları, PerfView'da çöp toplama (GC) istatistikleri gözden geçirilerek ve incelenerek tanılanabilir:

  • Çöp toplama duraklatma süresi.
  • Atık toplamada işlemci zamanının yüzde kaçı harcandı?
  • Kaç çöp toplaması 0, 1 ve 2. nesildir.

Daha fazla bilgi için bkz . Çöp Toplama ve Performans.

Veri erişimini ve G/Ç'leri iyileştirme

Veri deposu ve diğer uzak hizmetlerle etkileşimler genellikle ASP.NET Core uygulamasının en yavaş parçalarıdır. Verileri verimli bir şekilde okumak ve yazmak iyi performans için kritik öneme sahiptir.

Öneriler:

  • Tüm veri erişim API'lerini zaman uyumsuz olarak çağırmayın .
  • Gerekenden daha fazla veri almayın. Yalnızca geçerli HTTP isteği için gerekli olan verileri döndürmek için sorgular yazın.
  • Biraz eski veriler kabul edilebilirse bir veritabanından veya uzak hizmetten alınan sık erişilen verileri önbelleğe almayı göz önünde bulundurun. Senaryoya bağlı olarak MemoryCache veya DistributedCache kullanın. Daha fazla bilgi için bkz . ASP.NET Core'da yanıt önbelleğe alma.
  • Ağ gidiş dönüşlerini en aza indirin. Amaç, gerekli verileri birkaç çağrı yerine tek bir çağrıda almaktır.
  • Salt okunur amaçlarla verilere erişirken Entity Framework Core'da izleme olmayan sorgular kullanın. EF Core izlemesiz sorguların sonuçlarını daha verimli bir şekilde döndürebilir.
  • Filtrelemenin veritabanı tarafından gerçekleştirilmesi için LINQ sorgularını (örneğin, , .Selectveya .Sum deyimleri ile.Where) filtreleyin ve birleştirin.
  • İstemcideki bazı sorgu işleçlerini çözümlediğini EF Core ve bunun verimsiz sorgu yürütmeye yol açabileceğini göz önünde bulundurun. Daha fazla bilgi için bkz . İstemci değerlendirme performansı sorunları.
  • "N + 1" SQL sorgularının yürütülmesine neden olabilecek yansıtma sorgularını koleksiyonlarda kullanmayın. Daha fazla bilgi için bkz . Bağıntılı alt sorguları iyileştirme.

Aşağıdaki yaklaşımlar yüksek ölçekli uygulamalarda performansı artırabilir:

Kod tabanını işlemeden önce yukarıdaki yüksek performanslı yaklaşımların etkisini ölçmenizi öneririz. Derlenen sorguların ek karmaşıklığı performans iyileştirmesini haklı çıkarmayabilir.

Uygulama Analizler veya profil oluşturma araçlarıyla verilere erişmek için harcanan süre gözden geçirilerek sorgu sorunları algılanabilir. Veritabanlarının çoğu sık yürütülen sorgularla ilgili istatistikleri de kullanılabilir hale getirir.

HttpClientFactory ile HTTP bağlantılarını havuza oluşturma

Arabirimi uygulasa HttpClientIDisposable da yeniden kullanım için tasarlanmıştır. Kapalı HttpClient örnekler, yuvaları kısa bir süre için durumda açık TIME_WAIT bırakır. Nesneleri oluşturan ve atan HttpClient bir kod yolu sık sık kullanılıyorsa, uygulama kullanılabilir yuvaları tüketebilir. HttpClientFactory bu sorunun çözümü olarak ASP.NET Core 2.1'de kullanıma sunulmuştur. Performansı ve güvenilirliği iyileştirmek için HTTP bağlantılarını havuza alma işlemini gerçekleştirir. Daha fazla bilgi için bkz. Dayanıklı HTTP isteklerini uygulamak için kullanmaHttpClientFactory.

Öneriler:

  • Örnekleri doğrudan oluşturmayın HttpClient ve atmayın.
  • Örnekleri almak HttpClient için HttpClientFactory kullanın. Daha fazla bilgi için bkz . Dayanıklı HTTP isteklerini uygulamak için HttpClientFactory kullanma.

Ortak kod yollarını hızlı tutma

Tüm kodunuzun hızlı olmasını istiyorsunuz. En iyi duruma getirmenin en önemlileri sık kullanılan kod yollarıdır. Bu modüller şunlardır:

  • Uygulamanın istek işleme işlem hattındaki ara yazılım bileşenleri, özellikle ara yazılım işlem hattında erken çalıştırılır. Bu bileşenlerin performans üzerinde büyük etkisi vardır.
  • Her istek için veya istek başına birden çok kez yürütülen kod. Örneğin, özel günlük kaydı, yetkilendirme işleyicileri veya geçici hizmetlerin başlatılması.

Öneriler:

  • Uzun süre çalışan görevlerle özel ara yazılım bileşenleri kullanmayın.
  • Sık erişimli kod yollarını tanımlamak için Visual Studio Tanılama Araçları veya PerfView gibi performans profili oluşturma araçlarını kullanın.

HTTP isteklerinin dışında uzun süre çalışan Görevleri tamamlama

ASP.NET Core uygulamasına yönelik isteklerin çoğu, gerekli hizmetleri çağıran ve HTTP yanıtı döndüren bir denetleyici veya sayfa modeli tarafından işlenebilir. Uzun süre çalışan görevler içeren bazı istekler için istek-yanıt işleminin tamamını zaman uyumsuz hale getirmek daha iyidir.

Öneriler:

  • Normal HTTP isteği işleme kapsamında uzun süre çalışan görevlerin tamamlanmasını beklemeyin.
  • Azure İşlevi ile arka plan hizmetleriyleuzun süre çalışan istekleri veya işlem dışı istekleri işlemeyi göz önünde bulundurun. İşlem dışı çalışmayı tamamlamak, yoğun CPU kullanan görevler için özellikle yararlıdır.
  • İstemcilerle zaman uyumsuz olarak iletişim kurmak için gibi SignalRgerçek zamanlı iletişim seçeneklerini kullanın.

İstemci varlıklarını küçültme

ASP.NET Karmaşık ön uçlara sahip Core uygulamaları genellikle birçok JavaScript, CSS veya görüntü dosyasına hizmet eder. İlk yük isteklerinin performansı şu şekilde geliştirilebilir:

  • Paketleme, birden çok dosyayı tek bir dosyada birleştirir.
  • Küçültme, boşlukları ve açıklamaları kaldırarak dosyaların boyutunu küçültür.

Öneriler:

  • Uyumlu araçlardan bahseden ve hem hem de Development ortamları işlemek için ASP.NET Core environment etiketinin nasıl kullanılacağını gösteren paketleme ve Production küçültme yönergelerini kullanın.
  • Karmaşık istemci varlık yönetimi için Webpack gibi diğer üçüncü taraf araçları göz önünde bulundurun.

Yanıtları sıkıştırma

Yanıtın boyutunu azaltmak genellikle bir uygulamanın yanıt hızını önemli ölçüde artırır. Yük boyutlarını azaltmanın bir yolu, bir uygulamanın yanıtlarını sıkıştırmaktır. Daha fazla bilgi için bkz . Yanıt sıkıştırma.

En son ASP.NET Core sürümünü kullanma

ASP.NET Core'un her yeni sürümü performans geliştirmeleri içerir. .NET Core ve ASP.NET Core'daki iyileştirmeler, daha yeni sürümlerin genellikle eski sürümlerden daha iyi olduğu anlamına gelir. Örneğin, .NET Core 2.1 derlenmiş normal ifadeler için destek ekledi ve Span<T'den> yararlandı. ASP.NET Core 2.2, HTTP/2 desteği ekledi. ASP.NET Core 3.0, bellek kullanımını azaltan ve aktarım hızını geliştiren birçok geliştirme ekler. Performans öncelikliyse, ASP.NET Core'un geçerli sürümüne yükseltmeyi göz önünde bulundurun.

Özel durumları simge durumuna küçültme

Özel durumlar nadir olmalıdır. Özel durumları oluşturma ve yakalama, diğer kod akışı desenlerine göre yavaştır. Bu nedenle, normal program akışını denetlemek için özel durumlar kullanılmamalıdır.

Öneriler:

  • Özellikle sık erişimli kod yollarında, normal program akışının bir aracı olarak özel durumlar oluşturmayı veya yakalamayı kullanmayın.
  • Özel duruma neden olabilecek koşulları algılamak ve işlemek için uygulamaya mantık eklemeyin.
  • Olağan dışı veya beklenmeyen koşullar için özel durumlar oluşturarak veya yakalayın.

Uygulama Analizler gibi uygulama tanılama araçları, bir uygulamada performansı etkileyebilecek yaygın özel durumları belirlemeye yardımcı olabilir.

HttpRequest/HttpResponse gövdesinde zaman uyumlu okuma veya yazma işleminden kaçının

ASP.NET Core'daki tüm G/Ç zaman uyumsuzdur. Sunucular, hem zaman uyumlu hem de zaman uyumsuz aşırı yüklemeleri olan arabirimini uygular Stream . İş parçacığı havuzu iş parçacıklarını engellememek için zaman uyumsuz olanlar tercih edilmelidir. İş parçacıklarının engellenmesi, iş parçacığı havuzu aç kalmasına neden olabilir.

Bunu yapma: Aşağıdaki örnekte kullanılır ReadToEnd. Geçerli iş parçacığının sonucu beklemesini engeller. Bu, zaman uyumsuz eşitleme örneğidir.

public class BadStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public ActionResult<ContosoData> Get()
    {
        var json = new StreamReader(Request.Body).ReadToEnd();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }
}

Yukarıdaki kodda, Get HTTP isteği gövdesinin tamamını zaman uyumlu bir şekilde belleğe okur. İstemci yavaş karşıya yükleniyorsa, uygulama zaman uyumsuz olarak eşitleme yapıyordur. Zaman uyumlu okumaları desteklemediğinden uygulama zaman uyumsuz Kestrel olarak eşitlenir.

Bunu yapın: Aşağıdaki örnek okurken iş parçacığını kullanır ReadToEndAsync ve engellemez.

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        var json = await new StreamReader(Request.Body).ReadToEndAsync();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }

}

Yukarıdaki kod, HTTP isteği gövdesinin tamamını zaman uyumsuz olarak belleğe okur.

Uyarı

İstek büyükse, HTTP isteği gövdesinin tamamının belleğe okunması bellek yetersiz (OOM) koşuluna neden olabilir. OOM hizmet reddine neden olabilir. Daha fazla bilgi için bu makalenin büyük istek gövdelerini veya yanıt gövdelerini belleğe okumaktan kaçınma bölümüne bakın.

Bunu yapın: Aşağıdaki örnek, arabelleğe alınamayan bir istek gövdesi kullanılarak tamamen zaman uyumsuzdur:

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
    }
}

Yukarıdaki kod, istek gövdesini zaman uyumsuz olarak bir C# nesnesine seri hale getirmektedir.

Request.Form yerine ReadFormAsync'i tercih edin

yerine HttpContext.Request.FormkullanınHttpContext.Request.ReadFormAsync. HttpContext.Request.Form yalnızca aşağıdaki koşullarla güvenle okunabilir:

  • Form, çağrısı ReadFormAsynctarafından okundu ve
  • Önbelleğe alınan form değeri kullanılarak okunuyor HttpContext.Request.Form

Bunu yapma: Aşağıdaki örnekte kullanılır HttpContext.Request.Form. HttpContext.Request.Formeşitlemeyi zaman uyumsuz olarak kullanır ve iş parçacığı havuzu aç kalmasına neden olabilir.

public class BadReadController : Controller
{
    [HttpPost("/form-body")]
    public IActionResult Post()
    {
        var form =  HttpContext.Request.Form;

        Process(form["id"], form["name"]);

        return Accepted();
    }

Bunu yapın: Aşağıdaki örnek, form gövdesini zaman uyumsuz olarak okumak için kullanır HttpContext.Request.ReadFormAsync .

public class GoodReadController : Controller
{
    [HttpPost("/form-body")]
    public async Task<IActionResult> Post()
    {
       var form = await HttpContext.Request.ReadFormAsync();

        Process(form["id"], form["name"]);

        return Accepted();
    }

Büyük istek gövdelerini veya yanıt gövdelerini belleğe okumaktan kaçının

.NET'te, 85.000 bayttan büyük veya buna eşit her nesne ayırma işlemi büyük nesne yığınında (LOH) biter. Büyük nesneler iki şekilde pahalıdır:

  • Yeni ayrılan büyük bir nesnenin belleğinin temizlenmesi gerektiğinden ayırma maliyeti yüksektir. CLR, yeni ayrılan tüm nesneler için belleğin temizlendiğini garanti eder.
  • LOH, yığının geri kalanıyla birlikte toplanır. LOH tam bir çöp toplama veya 2. Nesil toplama gerektirir.

Bu blog gönderisinde sorunu kısaca açıklanmaktadır:

Büyük bir nesne ayrıldığında, 2. Nesil nesnesi olarak işaretlenir. Küçük nesneler için 0. Nesil değil. Bunun sonuçları, LOH'ta belleğiniz tükenirse GC'nin yalnızca LOH'u değil tüm yönetilen yığını temizlemesidir. Böylece LOH dahil 0. Nesil, 1. Nesil ve 2. Nesil'i temizler. Buna tam çöp toplama denir ve en çok zaman alan çöp toplama işlemidir. Birçok uygulama için kabul edilebilir. Ancak, ortalama bir web isteğini işlemek için birkaç büyük bellek arabelleğinin gerektiği yüksek performanslı web sunucuları için kesinlikle değildir (yuvadan okuma, açma, çözme JSAÇILMA ve daha fazlası).

Büyük bir istek veya yanıt gövdesini tek byte[] bir veya stringiçinde depolama:

  • LOH'ta hızla yer tükenmesine neden olabilir.
  • Çalışan tam GC'ler nedeniyle uygulama için performans sorunlarına neden olabilir.

Zaman uyumlu veri işleme API'siyle çalışma

Yalnızca zaman uyumlu okuma ve yazmaları destekleyen bir seri hale getirici/seri hale getirici kullanırken (örneğin, Json.NET):

  • Verileri seri hale getiriciye/seri hale getiriciyi kaldırmaya geçirmeden önce zaman uyumsuz olarak belleğe arabelleğe alın.

Uyarı

İstek büyükse bellek yetersiz (OOM) koşuluna yol açabilir. OOM hizmet reddine neden olabilir. Daha fazla bilgi için bu makalenin büyük istek gövdelerini veya yanıt gövdelerini belleğe okumaktan kaçınma bölümüne bakın.

ASP.NET Core 3.0, ON serileştirmesi için JSvarsayılan olarak kullanırSystem.Text.Json. System.Text.Json:

  • Zaman uyumsuz olarak ON'ı okur ve yazar JS.
  • UTF-8 metni için iyileştirilmiştir.
  • Genellikle değerinden Newtonsoft.Jsondaha yüksek bir performanstır.

IHttpContextAccessor.HttpContext'i bir alanda depolama

IHttpContextAccessor.HttpContext, istek iş parçacığından erişildiğinde etkin isteğin sonucunu döndürürHttpContext. IHttpContextAccessor.HttpContext bir alanda veya değişkende depolanmamalıdır.

Bunu yapma: Aşağıdaki örnek öğesini HttpContext bir alanda depolar ve daha sonra kullanmayı dener.

public class MyBadType
{
    private readonly HttpContext _context;
    public MyBadType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }

    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Yukarıdaki kod genellikle oluşturucuda null veya yanlış HttpContext yakalar.

Bunu yapın: Aşağıdaki örnek:

  • öğesini IHttpContextAccessor bir alanda depolar.
  • HttpContext alanını doğru zamanda kullanır ve için nulldenetler.
public class MyGoodType
{
    private readonly IHttpContextAccessor _accessor;
    public MyGoodType(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public void CheckAdmin()
    {
        var context = _accessor.HttpContext;
        if (context != null && !context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Birden çok iş parçacığından HttpContext'e erişmeyin

HttpContextparçacığı güvenli değildir . Birden çok iş parçacığından paralel olarak erişim HttpContext , kilitlenmeler, kilitlenmeler ve veri bozulması gibi beklenmeyen davranışlara neden olabilir.

Bunu yapma: Aşağıdaki örnek üç paralel istek yapar ve giden HTTP isteğinden önce ve sonra gelen istek yolunu günlüğe kaydeder. İstek yoluna birden çok iş parçacığından ve büyük olasılıkla paralel olarak erişilir.

public class AsyncBadSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        var query1 = SearchAsync(SearchEngine.Google, query);
        var query2 = SearchAsync(SearchEngine.Bing, query);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }       

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.", 
                                    HttpContext.Request.Path);
            searchResults = _searchService.Search(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", 
                                    HttpContext.Request.Path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", 
                             HttpContext.Request.Path);
        }

        return await searchResults;
    }

Bunu yapın: Aşağıdaki örnek, üç paralel istek yapmadan önce gelen istekteki tüm verileri kopyalar.

public class AsyncGoodSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        string path = HttpContext.Request.Path;
        var query1 = SearchAsync(SearchEngine.Google, query,
                                 path);
        var query2 = SearchAsync(SearchEngine.Bing, query, path);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
                                                  string path)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.",
                                   path);
            searchResults = await _searchService.SearchAsync(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", path);
        }

        return await searchResults;
    }

İstek tamamlandıktan sonra HttpContext'i kullanmayın

HttpContext yalnızca ASP.NET Core işlem hattında etkin bir HTTP isteği olduğu sürece geçerlidir. ASP.NET Core işlem hattının tamamı, her isteği yürüten zaman uyumsuz bir temsilci zinciridir. Task Bu zincirden döndürülen tamamlandığında, HttpContext geri dönüştürülür.

Bunu yapma: Aşağıdaki örnek, http isteğinin ilke await ulaşıldığında tamamlanmasını sağlayan bir örnek kullanırasync void:

  • kullanmakasync void, ASP.NET Core uygulamalarında HER ZAMAN kötü bir uygulamadır.
  • Örnek kod, HTTP isteği tamamlandıktan sonra öğesine HttpResponse erişir.
  • Geç erişim işlemi kilitler.
public class AsyncBadVoidController : Controller
{
    [HttpGet("/async")]
    public async void Get()
    {
        await Task.Delay(1000);

        // The following line will crash the process because of writing after the 
        // response has completed on a background thread. Notice async void Get()

        await Response.WriteAsync("Hello World");
    }
}

Bunu yapın: Aşağıdaki örnek, http isteğinin eylem tamamlanana kadar tamamlanmaması için çerçeveye bir Task döndürür.

public class AsyncGoodTaskController : Controller
{
    [HttpGet("/async")]
    public async Task Get()
    {
        await Task.Delay(1000);

        await Response.WriteAsync("Hello World");
    }
}

HttpContext'i arka plan iş parçacıklarında yakalama

Bunu yapma: Aşağıdaki örnekte kapatma özelliğinin yakalaması HttpContextController gösterilmektedir. İş öğesi şu şekilde olabileceğinden bu kötü bir uygulamadır:

  • İstek kapsamının dışında çalıştırın.
  • Yanlış HttpContextokumaya çalış.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        var path = HttpContext.Request.Path;
        Log(path);
    });

    return Accepted();
}

Bunu yapın: Aşağıdaki örnek:

  • İstek sırasında arka plan görevinde gerekli olan verileri kopyalar.
  • Denetleyiciden hiçbir şeye başvurmaz.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
    string path = HttpContext.Request.Path;
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        Log(path);
    });

    return Accepted();
}

Arka plan görevleri barındırılan hizmetler olarak uygulanmalıdır. Daha fazla bilgi için bkz . Barındırılan hizmetlerle arka plan görevleri.

Arka plan iş parçacıklarında denetleyicilere eklenen hizmetleri yakalama

Bunu yapma: Aşağıdaki örnekte, eylem parametresinden Controller öğesini yakalayan DbContext bir kapatma gösterilmektedir. Bu kötü bir uygulama. İş öğesi istek kapsamının dışında çalıştırılabilir. ContosoDbContext, isteğin kapsamına alınır ve sonuçta bir ObjectDisposedExceptionelde edilir.

[HttpGet("/fire-and-forget-1")]
public IActionResult FireAndForget1([FromServices]ContosoDbContext context)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        context.Contoso.Add(new Contoso());
        await context.SaveChangesAsync();
    });

    return Accepted();
}

Bunu yapın: Aşağıdaki örnek:

  • IServiceScopeFactory Arka plan iş öğesinde kapsam oluşturmak için bir ekler. IServiceScopeFactory tekildir.
  • Arka plan iş parçacığında yeni bir bağımlılık ekleme kapsamı oluşturur.
  • Denetleyiciden hiçbir şeye başvurmaz.
  • Gelen istekten öğesini yakalamaz ContosoDbContext .
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        await using (var scope = serviceScopeFactory.CreateAsyncScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

Aşağıdaki vurgulanmış kod:

  • Arka plan işleminin ömrü için bir kapsam oluşturur ve bu kapsamdaki hizmetleri çözümler.
  • Doğru kapsamdan kullanır ContosoDbContext .
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        await using (var scope = serviceScopeFactory.CreateAsyncScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

Yanıt gövdesi başlatıldıktan sonra durum kodunu veya üst bilgileri değiştirmeyin

ASP.NET Core, HTTP yanıt gövdesini arabelleğe almaz. Yanıt ilk kez yazılıyor:

  • Üst bilgiler, gövdenin bu öbekleriyle birlikte istemciye gönderilir.
  • Yanıt üst bilgilerini değiştirmek artık mümkün değildir.

Bunu yapma: Aşağıdaki kod, yanıt başlatıldıktan sonra yanıt üst bilgilerini eklemeye çalışır:

app.Use(async (context, next) =>
{
    await next();

    context.Response.Headers["test"] = "test value";
});

Yukarıdaki kodda, context.Response.Headers["test"] = "test value"; yanıta yazılmışsa next() bir özel durum oluşturur.

Bunu yapın: Aşağıdaki örnek, üst bilgileri değiştirmeden önce HTTP yanıtının başlatılıp başlatılmadığını denetler.

app.Use(async (context, next) =>
{
    await next();

    if (!context.Response.HasStarted)
    {
        context.Response.Headers["test"] = "test value";
    }
});

Bunu yapın: Aşağıdaki örnek, yanıt üst bilgileri istemciye boşaltilmeden önce üst bilgileri ayarlamak için kullanır HttpResponse.OnStarting .

Yanıtın başlatılmadığını denetlemek, yanıt üst bilgileri yazılmadan hemen önce çağrılacak bir geri çağırmanın kaydedilmesine olanak tanır. Yanıtın başlatılmadığını denetleme:

  • Üst bilgileri tam zamanında ekleme veya geçersiz kılma olanağı sağlar.
  • İşlem hattındaki bir sonraki ara yazılım hakkında bilgi sahibi olmayı gerektirmez.
app.Use(async (context, next) =>
{
    context.Response.OnStarting(() =>
    {
        context.Response.Headers["someheader"] = "somevalue";
        return Task.CompletedTask;
    });

    await next();
});

Yanıt gövdesine yazmaya başladıysanız next() çağrısı yapma

Bileşenler yalnızca yanıtı işleyip işlemeleri mümkünse çağrılmasını bekler.

IIS ile işlem içi barındırma kullanma

İşlem içi barındırmayı kullanarak ASP.NET Core uygulaması kendi IIS çalışan işlemiyle aynı işlemde çalıştırılır. İşlem içi barındırma, istekler geri döngü bağdaştırıcısı üzerinden proksid olmadığından işlem dışı barındırmada gelişmiş performans sağlar. Geri döngü bağdaştırıcısı, giden ağ trafiğini aynı makineye geri döndüren bir ağ arabirimidir. IIS işlem yönetimini Windows İşlem Etkinleştirme Hizmeti (WAS) ile işler.

Projeler, ASP.NET Core 3.0 ve sonraki sürümlerde işlem içi barındırma modelini varsayılan olarak kullanır.

Daha fazla bilgi için bkz. IIS ile Windows'ta Konak ASP.NET Core

HttpRequest.ContentLength değerinin null olmadığını varsaymayın

HttpRequest.ContentLength üst bilgi alınmazsa Content-Length null olur. Bu durumda null, istek gövdesinin uzunluğunun bilinmediği anlamına gelir; bu, uzunluğun sıfır olduğu anlamına gelmez. Null (hariç ==) ile yapılan tüm karşılaştırmalar false döndüreceği için, örneğin, istek gövdesi boyutu 1024'ten fazla olduğunda karşılaştırma Request.ContentLength > 1024döndürülebilir false . Bunu bilmemek uygulamalarda güvenlik açıklarına yol açabilir. Çok büyük isteklere karşı koruma sağlamadığınızı düşünebilirsiniz.

Daha fazla bilgi için bu StackOverflow yanıtına bakın.

Güvenilir web uygulaması desenleri

İster sıfırdan ister mevcut bir uygulamayı yeniden düzenleme olsun, modern, güvenilir, performanslı, test edilebilir, uygun maliyetli ve ölçeklenebilir bir ASP.NET Core uygulaması oluşturma yönergeleri için YouTube videolarınıve makalesini for.NET Güvenilir Web Uygulaması Düzeni'nebakın.