bästa praxis för ASP.NET Core
Anteckning
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. För den aktuella versionen, se .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Av Mike Rousos
Den här artikeln innehåller riktlinjer för att maximera prestanda och tillförlitlighet för ASP.NET Core-appar.
Cachelagrat aggressivt
Cachelagring beskrivs i flera delar av den här artikeln. Mer information finns i Översikt över cachelagring i ASP.NET Core.
Förstå prestandakritiska kodvägar
I den här artikeln definieras en het kodsökväg som en kodsökväg som ofta anropas och där mycket av exekveringstiden spenderas. Snabbkodssökvägar begränsar vanligtvis utskalning och prestanda för appar och beskrivs i flera delar av den här artikeln.
Undvik att blockera samtal
ASP.NET Core-appar bör utformas för att bearbeta många begäranden samtidigt. Asynkrona API:er gör det möjligt för en liten pool med trådar att hantera tusentals samtidiga begäranden genom att inte vänta på blockeringsanrop. I stället för att vänta på att en långvarig synkron uppgift ska slutföras kan tråden fungera på en annan begäran.
Ett vanligt prestandaproblem i ASP.NET Core-appar blockerar anrop som kan vara asynkrona. Många synkrona blockeringsanrop leder till utsvulten trådpool och försämrade svarstider.
Blockera inte asynkron exekvering genom att anropa Task.Wait eller Task<TResult>.Result.
Skaffa inte lås i vanliga kodsökvägar. ASP.NET Core-appar fungerar bäst när de utformas för att köra kod parallellt.
Ring inteTask.Run och vänta omedelbart på det. ASP.NET Core kör redan appkod på vanliga trådpoolstrådar, så att anropa Task.Run
resulterar bara i extra onödig schemaläggning av trådpooler. Även om den schemalagda koden skulle blockera en tråd förhindrar Task.Run
inte det.
- Görsnabbkodssökvägar asynkrona.
- Anropa API:er för dataåtkomst, I/O och långvariga åtgärder asynkront om ett asynkront API är tillgängligt.
- Använd inteTask.Run för att göra ett synkront API asynkront.
- Se till att kontrollerns/Razor sidåtgärder är asynkrona. Hela anropsstacken är asynkron för att dra nytta av async/await-mönster.
- Överväg att använda meddelandemäklare som Azure Service Bus för att avlasta långvariga samtal
En profilerare, till exempel PerfView, kan användas för att hitta trådar som ofta läggs till i Trådpool. Händelsen Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start
anger en tråd som lagts till i trådpoolen.
Returnera stora samlingar över flera mindre sidor
En webbsida bör inte läsa in stora mängder data samtidigt. När du returnerar en samling objekt bör du överväga om det kan leda till prestandaproblem. Kontrollera om designen kan ge följande dåliga resultat:
- OutOfMemoryException eller hög minnesförbrukning
- Utsvulten trådpool (se följande kommentarer om IAsyncEnumerable<T>)
- Långsamma svarstider
- Frekvent skräpinsamling
Lägg till paginering för att mildra föregående scenarier. Med hjälp av sidstorleks- och sidindexparametrar bör utvecklare prioritera utformningen av att returnera ett partiellt resultat. När ett uttömmande resultat krävs bör sidnumrering användas för att asynkront fylla i batchar med resultat för att undvika att låsa serverresurser.
Mer information om paginering och begränsning av antalet returnerade poster, se:
Returnera IEnumerable<T>
eller IAsyncEnumerable<T>
Om du returnerar IEnumerable<T>
från en åtgärd resulterar det i synkron samlingsiteration av serialisatorn. Resultatet är blockering av anrop och en potential för utsvulten trådpool. Undvik synkron uppräkning genom att använda ToListAsync
innan du returnerar den uppräkningsbara koden.
Från och med ASP.NET Core 3.0 kan IAsyncEnumerable<T>
användas som ett alternativ till IEnumerable<T>
som räknas upp asynkront. Mer information finns i Controller-åtgärdsreturtyper.
Minimera stora objektallokeringar
.NET Core-skräpinsamlaren hanterar allokering och frigörande av minne automatiskt i ASP.NET Core-appar. Automatisk skräpinsamling innebär i allmänhet att utvecklare inte behöver oroa sig för hur eller när minnet frigörs. Det tar dock processortid att rensa ouppnåeliga objekt, så utvecklare bör minimera allokeringen av objekt i sökvägar för frekvent kod. Skräpinsamling är särskilt dyrt för stora objekt (>= 85 000 byte). Stora objekt lagras på stora objekthögen och kräver en fullständig (generation 2) skräpinsamling för att rensa. Till skillnad från generation 0- och generation 1-samlingar kräver en generation 2-samling en tillfällig avstängning av appkörningen. Frekvent allokering och avallokering av stora objekt kan orsaka inkonsekventa prestanda.
Rekommendationer:
- Överväg att cachelagra stora objekt som används ofta. Cachelagring av stora objekt förhindrar dyra allokeringar.
- Gör poolbuffertar genom att använda en ArrayPool<T> för att lagra stora matriser.
- Allokera inte många, kortlivade stora objekt på heta kodvägar.
Minnesproblem, till exempel föregående, kan diagnostiseras genom att granska skräpinsamlingsstatistik (GC) i PerfView- och undersöka:
- Paustid för skräpinsamling.
- Vilken procentandel av processortiden som spenderas i skräpinsamling.
- Hur många skräpinsamlingar finns det av generation 0, 1 och 2?
Mer information finns i skräpinsamling och prestanda.
Optimera dataåtkomst och I/O
Interaktioner med ett datalager och andra fjärrtjänster är ofta de långsammaste delarna i en ASP.NET Core-app. Det är viktigt att läsa och skriva data effektivt för att få bra prestanda.
Rekommendationer:
- Anropa alla API:er för dataåtkomst asynkront.
- Hämta inte mer data än vad som är nödvändigt. Skriv frågor för att returnera bara de data som behövs för den aktuella HTTP-begäran.
- Överväg cachelagring av data som hämtas ofta från en databas eller fjärrtjänst om något inaktuella data är godtagbara. Beroende på scenariot använder du en MemoryCache- eller en DistributedCache-. Mer information finns i cachelagring av -svar i ASP.NET Core.
- Minimera nätverksresor. Målet är att hämta nödvändiga data i ett enda anrop i stället för flera anrop.
- Använderfrågor utan spårning i Entity Framework Core vid åtkomst till data i skrivskyddade syften. EF Core kan returnera resultatet av frågor utan spårning mer effektivt.
-
Filtrera och aggregera LINQ-frågor (med till exempel
.Where
,.Select
eller.Sum
-instruktioner) så att filtreringen utförs av databasen. - Anser att EF Core löser vissa frågeoperatorer på klienten, vilket kan leda till ineffektiv frågekörning. Mer information finns i Problem med klientutvärderingsprestanda.
- Använd inte projektionsfrågor i samlingar, vilket kan leda till att SQL-frågor "N + 1" körs. Mer information finns i optimering av korrelerade underfrågor.
Följande metoder kan förbättra prestanda i storskaliga appar:
Vi rekommenderar att du mäter effekten av de föregående metoderna med höga prestanda innan du genomför kodbasen. Den ytterligare komplexiteten för kompilerade frågor kanske inte motiverar prestandaförbättringen.
Frågeproblem kan identifieras genom att granska den tid som används för att komma åt data med Application Insights eller med profileringsverktyg. De flesta databaser gör också statistik tillgänglig för frågor som körs ofta.
Pool-HTTP-anslutningar med HttpClientFactory
Även om HttpClient implementerar IDisposable
-gränssnittet är det utformat för återanvändning. Stängda HttpClient
instanser lämnar socketar öppna i TIME_WAIT
tillstånd under en kort tidsperiod. Om en kodsökväg som skapar och bortskaffar HttpClient
objekt används ofta kan appen uttömma tillgängliga socketar.
HttpClientFactory
introducerades i ASP.NET Core 2.1 som en lösning på det här problemet. Den hanterar poolning av HTTP-anslutningar för att optimera prestanda och tillförlitlighet. Mer information finns i Använda HttpClientFactory
för att implementera elastiska HTTP-begäranden.
Rekommendationer:
-
inte skapa och ta bort
HttpClient
instanser direkt. -
AnvändHttpClientFactory för att hämta
HttpClient
instanser. Mer information finns i Använda HttpClientFactory för att implementera elastiska HTTP-begäranden.
Håll vanliga kodvägar snabba
Du vill att all kod ska vara snabb. Frekvent anropade kodvägar är de mest kritiska att optimera. Dessa inkluderar:
- Mellanprogramskomponenter i appens pipeline för bearbetning av begäranden, särskilt mellanprogram körs tidigt i pipelinen. Dessa komponenter har stor inverkan på prestanda.
- Kod som körs för varje begäran eller flera gånger per begäran. Till exempel anpassad loggning, auktoriseringshanterare eller initiering av tillfälliga tjänster.
Rekommendationer:
- Använd inte anpassade mellanprogramskomponenter med långvariga uppgifter.
- Använd verktyg för prestandaprofilering, till exempel Visual Studio Diagnostic Tools eller PerfView), för att identifiera sökvägar för snabbkod.
Slutför långvariga uppgifter utanför HTTP-begäranden
De flesta begäranden till en ASP.NET Core-app kan hanteras av en kontrollant eller sidmodell som anropar nödvändiga tjänster och returnerar ett HTTP-svar. För vissa begäranden som omfattar långvariga uppgifter är det bättre att göra hela processen för begärandesvar asynkron.
Rekommendationer:
- Vänta inte på att långvariga uppgifter ska slutföras som en del av vanlig BEARBETNING av HTTP-begäranden.
- Överväg att hantera långvariga begäranden med bakgrundstjänster eller utanför processen, eventuellt med en Azure Function och/eller använda en meddelandekö som Azure Service Bus. Att slutföra arbete utan process är särskilt fördelaktigt för CPU-intensiva uppgifter.
- Använd kommunikationsalternativ i realtid, till exempel SignalR, för att kommunicera med klienter asynkront.
Minimera klienttillgångar
ASP.NET Core-appar med komplexa klientdelar betjänar ofta många JavaScript-, CSS- eller bildfiler. Prestanda för inledande inläsningsbegäranden kan förbättras genom att:
- Paketering, som kombinerar flera filer i en.
- Minimera, vilket minskar storleken på filer genom att ta bort blanksteg och kommentarer.
Rekommendationer:
-
Använd riktlinjerna för kombinations- och minifiering, som nämner kompatibla verktyg och visar hur du använder ASP.NET Cores
environment
-tagg för att hantera bådeDevelopment
ochProduction
miljöer. - Överväg andra verktyg från tredje part, till exempel Webpack, för komplex hantering av klienttillgång.
Komprimera svar
Att minska storleken på svaret ökar vanligtvis svarstiden för en app, ofta dramatiskt. Ett sätt att minska nyttolaststorlekarna är att komprimera en apps svar. För mer information, se Response compression.
Använd den senaste ASP.NET Core-versionen
Varje ny version av ASP.NET Core innehåller prestandaförbättringar. Optimeringar i .NET Core och ASP.NET Core innebär att nyare versioner i allmänhet överträffar äldre versioner. Till exempel har .NET Core 2.1 lagt till stöd för kompilerade reguljära uttryck och dragit nytta av Span<T>. ASP.NET Core 2.2 har lagt till stöd för HTTP/2. ASP.NET Core 3.0 lägger till många förbättringar som minskar minnesanvändningen och förbättrar dataflödet. Om prestanda är en prioritet bör du överväga att uppgradera till den aktuella versionen av ASP.NET Core.
Minimera undantag
Undantag bör vara sällsynta. Det går långsamt att generera och fånga undantag i förhållande till andra kodflödesmönster. På grund av detta bör undantag inte användas för att styra det normala programflödet.
Rekommendationer:
- Använd inte att utlösa eller fånga undantag som ett sätt att använda normalt programflöde, särskilt i heta kodsökvägar.
- Inkludera logik i appen för att identifiera och hantera villkor som skulle orsaka ett undantag.
- Gör kasta eller fånga undantag för ovanliga eller oväntade förhållanden.
Appdiagnostikverktyg, till exempel Application Insights, kan hjälpa dig att identifiera vanliga undantag i en app som kan påverka prestanda.
Undvik synkron läsning eller skrivning på HttpRequest/HttpResponse-brödtext
Alla I/O i ASP.NET Core är asynkrona. Servrar implementerar Stream
-gränssnittet, som har både synkrona och asynkrona överlagringar. De asynkrona bör föredras för att undvika blockering av trådpoolens trådar. Blockering av trådar kan leda till utsvulten trådpool.
Gör inte så här: I följande exempel används ReadToEnd. Den blockerar den aktuella tråden för att vänta på resultatet. Det här är ett exempel på synk över asynk.
public class BadStreamReaderController : Controller
{
[HttpGet("/contoso")]
public ActionResult<ContosoData> Get()
{
var json = new StreamReader(Request.Body).ReadToEnd();
return JsonSerializer.Deserialize<ContosoData>(json);
}
}
I föregående kod läser Get
synkront hela HTTP-begärandetexten i minnet. Om klienten laddar upp långsamt genomför appen synkronisering över asynkrona metoder. Appen hanterar synkronisering asynkront eftersom KestrelINTE stöder synkrona läsningar.
Gör så här: Följande exempel använder ReadToEndAsync och blockerar inte tråden vid läsning.
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);
}
}
Föregående kod läser asynkront hela HTTP-begärandetexten i minnet.
Varning
Om begäran är stor kan det leda till ett minnesbristtillstånd om hela HTTP-begärandetexten läses in i minnet. OOM kan resultera i en Denial Of Service. Mer information finns i Undvik att läsa stora begärandeorgan eller svarskroppar i minnet i den här artikeln.
Gör detta: Följande exempel är helt asynkront med hjälp av en icke-buffrad begärandetext:
public class GoodStreamReaderController : Controller
{
[HttpGet("/contoso")]
public async Task<ActionResult<ContosoData>> Get()
{
return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
}
}
Föregående kod av-serialiserar asynkront begärandetexten till ett C#-objekt.
Föredra ReadFormAsync framför Request.Form
Använd HttpContext.Request.ReadFormAsync
i stället för HttpContext.Request.Form
.
HttpContext.Request.Form
kan läsas säkert endast under följande villkor:
- formuläret har lästs in genom ett anrop till
ReadFormAsync
, och - Det cachelagrade formulärvärdet läss med hjälp av
HttpContext.Request.Form
Gör inte så här: I följande exempel används HttpContext.Request.Form
.
HttpContext.Request.Form
använder synkronisering över asynk och kan leda till utarmning av trådpool.
public class BadReadController : Controller
{
[HttpPost("/form-body")]
public IActionResult Post()
{
var form = HttpContext.Request.Form;
Process(form["id"], form["name"]);
return Accepted();
}
Gör så här: I följande exempel används HttpContext.Request.ReadFormAsync
för att läsa formulärtexten asynkront.
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();
}
Undvik att läsa stora begärandekroppar eller svarskroppar i minnet
I .NET hamnar varje objektallokering som är större än eller lika med 85 000 byte i stora objekthögen (LOH). Stora objekt är dyra på två sätt:
- Allokeringskostnaden är hög eftersom minnet för ett nyligen allokerat stort objekt måste rensas. CLR garanterar att minnet för alla nyligen allokerade objekt rensas.
- LOH samlas ihop med resten av heapen. LOH kräver en fullständig skräpsamling eller Gen2-samling.
Det här blogginlägget beskriver problemet kortfattat:
När ett stort objekt allokeras markeras det som Gen 2-objekt. Inte Gen 0 som för små objekt. Konsekvenserna är att om du får slut på minne i LOH rensar GC upp hela den hanterade heapen, inte bara LOH. Så det rensar upp Gen 0, Gen 1 och Gen 2 inklusive LOH. Detta kallas fullständig skräpinsamling och är den mest tidskrävande skräpinsamlingen. För många program kan det vara acceptabelt. Men definitivt inte för högpresterande webbservrar, där få stora minnesbuffertar behövs för att hantera en genomsnittlig webbbegäran (läsa från en socket, dekomprimera, avkoda JSON med mera).
Lagra en stor begäran eller svarstext i en enda byte[]
eller string
:
- Kan leda till att utrymmet i LOH snabbt börjar ta slut.
- Kan orsaka prestandaproblem för appen på grund av att fullständiga GCs körs.
Arbeta med ett synkront API för databearbetning
När du använder en serialiserare/de-serialiserare som endast stöder synkrona läsningar och skrivningar (till exempel Json.NET):
- Buffring av data till minnet asynkront innan de skickas till serialiseraren/de-serialiseraren.
Varning
Om begäran är stor kan det leda till minnesbrist (out of memory). OOM kan resultera i en Denial Of Service. För mer information, se Undvik att läsa in stora begärandekroppar eller svarskroppar i minnet i den här artikeln.
ASP.NET Core 3.0 använder som standard System.Text.Json för JSON-serialisering. System.Text.Json:
- Läser och skriver JSON asynkront.
- Är optimerad för UTF-8-text.
- Normalt är prestandan högre än
Newtonsoft.Json
.
Lagra inte IHttpContextAccessor.HttpContext i ett fält
IHttpContextAccessor.HttpContext returnerar HttpContext
för den aktiva begäran när den nås från begärandetråden.
IHttpContextAccessor.HttpContext
ska inte lagras i ett fält eller en variabel.
Gör inte så här: Följande exempel lagrar HttpContext
i ett fält och försöker sedan använda det senare.
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");
}
}
}
Föregående kod hanterar ofta en null eller en felaktig HttpContext
i konstruktorn.
Gör så här: Följande exempel:
- Lagrar IHttpContextAccessor i ett fält.
- Använder fältet
HttpContext
vid rätt tidpunkt och söker efternull
.
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");
}
}
}
Få inte åtkomst till HttpContext från flera trådar
HttpContext
är inte trådsäkert. Att komma åt HttpContext
från flera trådar parallellt kan resultera i oväntat beteende, till exempel att servern slutar svara, kraschar och skadade data.
Gör inte så här: I följande exempel görs tre parallella begäranden och den inkommande begärandesökvägen loggas före och efter den utgående HTTP-begäran. Begärandesökvägen nås från flera trådar, eventuellt parallellt.
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;
}
Gör detta: I följande exempel kopieras alla data från den inkommande begäran innan de tre parallella begärandena görs.
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;
}
Använd inte HttpContext när begäran har slutförts
HttpContext
är endast giltigt så länge det finns en aktiv HTTP-begäran i ASP.NET Core-pipelinen. Hela ASP.NET Core-pipeline är en asynkron kedja av delegater som kör varje förfrågan. När kedjan är slutförd och Task
har returnerats, återvinns HttpContext
.
Gör inte så här: I följande exempel används async void
som gör att HTTP-begäran slutförs när den första await
nås:
- Att använda
async void
är ALLTID en dålig praxis i ASP.NET Core-appar. - Exempelkoden kommer åt
HttpResponse
när HTTP-begäran har slutförts. - Den sena åtkomsten kraschar processen.
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");
}
}
Gör så här: I följande exempel returneras en Task
till ramverket, så HTTP-begäran slutförs inte förrän åtgärden har slutförts.
public class AsyncGoodTaskController : Controller
{
[HttpGet("/async")]
public async Task Get()
{
await Task.Delay(1000);
await Response.WriteAsync("Hello World");
}
}
Avbilda inte HttpContext i bakgrundstrådar
Gör inte så här: Följande exempel visar att en stängning fångar upp HttpContext
från egenskapen Controller
. Det här är en felaktig metod eftersom arbetsobjektet kan:
- Kör utanför begärandeomfånget.
- Försök att läsa fel
HttpContext
.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
var path = HttpContext.Request.Path;
Log(path);
});
return Accepted();
}
Gör så här: Följande exempel:
- Kopierar de data som krävs i bakgrundsaktiviteten under begäran.
- Refererar inte till något från kontrollern.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
string path = HttpContext.Request.Path;
_ = Task.Run(async () =>
{
await Task.Delay(1000);
Log(path);
});
return Accepted();
}
Bakgrundsaktiviteter bör implementeras som värdbaserade tjänster. Mer information finns i Bakgrundsaktiviteter med värdbaserade tjänster.
Samla inte in tjänster som matas in i styrenheterna på bakgrundstrådar
Gör inte så här: I följande exempel visas en stängning som fångar upp DbContext
från Controller
-åtgärdsparametern. Det här är en dålig praxis. Arbetsobjektet kan köras utanför begärandeomfånget.
ContosoDbContext
är begränsad till begäran, vilket resulterar i en ObjectDisposedException
.
[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();
}
Gör så här: Följande exempel:
- Infogar en IServiceScopeFactory för att skapa ett omfång i arbetsobjektet i bakgrunden.
IServiceScopeFactory
är en singleton. - Skapar ett nytt omfång för beroendeinjektion i en bakgrundstråd.
- Refererar inte till något från kontrollen.
- Samlar inte in
ContosoDbContext
från den inkommande begäran.
[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();
}
Följande markerade kod:
- Skapar ett omfång för bakgrundsåtgärdens livslängd och löser tjänster från den.
- Använder
ContosoDbContext
från rätt omfång.
[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();
}
Ändra inte statuskoden eller rubrikerna när svarstexten har startats
ASP.NET Core buffar inte HTTP-svarstexten. Första gången svaret skrivs:
- Rubrikerna skickas tillsammans med den delen av brödtexten till klienten.
- Det går inte längre att ändra svarshuvuden.
Gör inte så här: Följande kod försöker lägga till svarshuvuden när svaret redan har startat:
app.Use(async (context, next) =>
{
await next();
context.Response.Headers["test"] = "test value";
});
I föregående kod utlöser context.Response.Headers["test"] = "test value";
ett undantag om next()
har skrivit till svaret.
Gör så här: I följande exempel kontrolleras om HTTP-svaret har startats innan rubrikerna ändras.
app.Use(async (context, next) =>
{
await next();
if (!context.Response.HasStarted)
{
context.Response.Headers["test"] = "test value";
}
});
Gör detta: I följande exempel används HttpResponse.OnStarting
för att ställa in rubrikerna innan svarshuvudena skickas till klienten.
Om du kontrollerar om svaret inte har startats kan du registrera ett återanrop som anropas precis innan svarsrubrikerna skrivs. Kontrollerar om svaret inte har startats:
- Ger möjlighet att lägga till eller åsidosätta rubriker precis i tid.
- Kräver inte kunskap om nästa mellanprogram i pipelinen.
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers["someheader"] = "somevalue";
return Task.CompletedTask;
});
await next();
});
Anropa inte next() om du redan har börjat skriva till svarskroppen
Komponenter förväntar sig bara att anropas om det är möjligt för dem att hantera och ändra svaret.
Använda processervering med IIS
Med hjälp av in-process hosting körs en ASP.NET Core-app i samma process som dess IIS-arbetsprocess. Inprocesshosting ger bättre prestanda jämfört med out-of-processvärd eftersom begäranden inte dirigeras via loopbackadaptern. Loopback-kortet är ett nätverksgränssnitt som returnerar utgående nätverkstrafik tillbaka till samma dator. IIS hanterar processhantering med Windows Process Activation Service (WAS).
Projekt använder som standard värdmodellen i process i ASP.NET Core 3.0 och senare.
Mer information finns i Host ASP.NET Core on Windows with IIS
Anta inte att HttpRequest.ContentLength inte är null
HttpRequest.ContentLength
är nullvärde om Content-Length
-huvudet inte tas emot. Null innebär i så fall att längden på begärandetexten inte är känd. Det betyder inte att längden är noll. Eftersom alla jämförelser med null (förutom ==
) returnerar false kan jämförelsen Request.ContentLength > 1024
, till exempel, returnera false
när begärandetextens storlek är större än 1024. Att inte veta detta kan leda till säkerhetshål i appar. Du kanske tror att du skyddar mot för stora begäranden när du inte gör det.
Mer information finns i det här StackOverflow-svaret.
Mönster för företagswebbappar
Vägledning om hur du skapar en tillförlitlig, säker, högpresterande, testbar och skalbar ASP.NET Core-app finns i Enterprise-webbappmönster. En komplett exempelwebbapp av produktionskvalitet som implementerar mönstren är tillgänglig.
ASP.NET Core