Scott Hanselman

ASP.NET Core Diagnostic Scenarios

September 30, 2021 Comment on this post [2] Posted in ASP.NET | DotNetCore | Open Source
Sponsored By

ThreadingDavid and friends has a great repository filled with examples of "broken patterns" in ASP.NET Core applications. It's a fantastic learning resource with both markdown and code that covers a number of common areas when writing scalable services in ASP.NET Core. Some of the guidance is general purpose but is explained through the lens of writing web services.

Here's a few great DON'T and DO examples, but be sure to Star the repo and check it out for yourself! This is somewhat advanced stuff but if you are doing high output low latency web services AT SCALE these tips will make a huge difference when you're doing a something a hundred thousand time a second!

DON'T - This example uses the legacy WebClient to make a synchronous HTTP request.

public string DoSomethingAsync()
{
var client = new WebClient();
return client.DownloadString(http://www.google.com);
}

DO - This example uses an HttpClient to asynchronously make an HTTP request.

static readonly HttpClient client = new HttpClient();

public Task<string> DoSomethingAsync()
{
return client.GetStringAsync("http://www.google.com");
}

Here's a list of ASP.NET Core Guidance. This one is fascinating. ASP.NET Core doesn't buffer responses which allows it to be VERY scalable. Massively so. As such you do need to be aware that things need to happen in a certain order - Headers come before Body, etc so you want to avoid adding headers after the HttpResponse has started.

DON'T - Add headers once you've started sending the body.

app.Use(async (next, context) =>
{
await context.Response.WriteAsync("Hello ");

await next();

// This may fail if next() already wrote to the response
context.Response.Headers["test"] = "value";
});

DO - Either check if it's started before you send the headers:

app.Use(async (next, context) =>
{
await context.Response.WriteAsync("Hello ");

await next();

// Check if the response has already started before adding header and writing
if (!context.Response.HasStarted)
{
context.Response.Headers["test"] = "value";
}
});

Or even BETTER, add the headers on the OnStarting call back to guarantee they are getting set.

app.Use(async (next, context) =>
{
// Wire up the callback that will fire just before the response headers are sent to the client.
context.Response.OnStarting(() =>
{
context.Response.Headers["test"] = "value";
return Task.CompletedTask;
});

await next();
});

There's a ton of great guidance around async programming. If you are returning something small or trivial, like a simple value, DON'T Task<>:

public class MyLibrary
{
public Task<int> AddAsync(int a, int b)
{
return Task.Run(() => a + b);
}
}

DO use ValueTask<> as this example not only doesn't use an extra threads and avoids heap allocation entirely:

public class MyLibrary
{
public ValueTask<int> AddAsync(int a, int b)
{
return new ValueTask<int>(a + b);
}
}

There's a ton of good learning over there so go check it out! https://github.com/davidfowl/AspNetCoreDiagnosticScenarios


Sponsor: Make login Auth0’s problem. Not yours. Provide the convenient login features your customers want, like social login, multi-factor authentication, single sign-on, passwordless, and more. Get started for free.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
October 08, 2021 13:07
If you are doing any serious work with httpClient, you should be getting your client instance with httpClientFactory: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
October 09, 2021 13:16
Thanks for sharing this example! I will surely practice this for acquiring my skill and knowledge on this type of core practice.

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.