High Performance
Coding with
.NET Core and C#
Shyma’a Qadoom..☺
2
Agenda
3
Every version comes with the latest, more
advanced capabilities and faster performance.
1.Use the
Latest Version When developing applications with ASP.NET Core.
Make sure you use the most current version of
ASP.NET Core. Since Microsoft constantly improves
its performance in the most recent version over
the earlier version.
ASP.NET Core
Performance Best
Practices
Understand hot code paths
a hot code path is defined as a code path
that is frequently called and where much of
the execution time occurs. Hot code paths
typically limit app scale-out and
performance.
Avoid blocking calls
ASP.NET Core apps should be designed to
process many requests simultaneously.
Asynchronous APIs allow a small pool of threads
to handle thousands of concurrent requests by
not waiting on blocking calls. Rather than
waiting on a long-running synchronous task to
complete, the thread can work on another
request.
A common performance problem in ASP.NET
Core apps is blocking calls
thatcould be asynchronous. Many
synchronous blocking calls lead
to Thread Pool starvation and degraded
response times.
• Do not:
• Block asynchronous execution by calling Task.Wait or
Task<RTesult>.Result. Acquire locks in common code paths. ASP.NET
Core apps are most performant when architected to run code in
parallel.
• Call Task.Run and immediately await it. ASP.NET Core already runs app
code on normal Thread Pool threads, so calling Task.Run only results in
extra unnecessary Thread Pool scheduling. Even if the scheduled code
would block a thread, Task.Run does not prevent that.
• Do:
• Make hot code paths asynchronous.
• Call data access, I/O, and long-running operations APIs
asynchronously if an asynchronous API is available. Do not use
Task.Run to make a synchronous API asynchronous.
• Make controller/Razor Page actions asynchronous. The entire call
stack is asynchronous in order to benefit from async/await
patterns.
Return Task<IEnumerable<T>>
or IAsyncEnumerable<T>
Both Task<IEnumerable<T>> and IAsyncEnumerable are used
to enumerate through data or go through a list of data. Yet there
is a huge difference. Task<IEnumerable<T>> provides records
once the data in collection is ready to send to the caller.
Whereas IAsyncEnumerable provides records as they are ready,
which mean it will send you record as its available rather than
waiting for the whole collection to be filled up. It provides you to
iterate a collection asynchronously using the yield keyword
which was not possible until C# 8.0.
It’s important to understand what thread is safe and what is not
safe when working with async enumerables.
Utilize JSON
Serialization
• ASP.NET Core 3.0 introduced System.Text.Json for JSON
serialization, enabling asynchronous JSON read and write
operations without waiting for other processes.
• This high-performance JSON serialization is more efficient
than Newtonsoft.Json. The System.Text.Json namespace
offers low allocation, compliant functionality, and efficient
JSON text serialization and deserialization.
8
9
Caching
Caching stands as a crucial tactic for boosting application
performance. It effectively diminishes the volume of requests
directed to the server, thereby augmenting response times.
Rather than repeatedly retrieving data from the server, the
preferred approach involves caching the initial response.
Subsequent requests are then compared with the cached
data for a match.
In the event that a match is discovered, no server call is
necessary because the data is obtained from the cache.
Reduce server calls and boost application performance by
implementing several caching strategies such as distributed
cache, in-memory caching, cache tag helper, and distributed
cache tag helper.
.NET Core Caching Best Practices
IDistributedCac
IMemoryCache
he
11
IMemoryCache
The mechanism is
very similar to
imemorycache.Runti
me.Caching.Memory
cache is a .NET 4
component. The
user interface is
quite simple.
IDistributedCache
12
The interface provides similar functionality to IMemoryCache, but there is some notable
difference.
• Additional async methods
• Refresh methods (which just reset sliding expirations without retrieving data as far as I can
tell)
• Byte-based
To summarize, rather than object-based
an in-memory cache is best when you need extremely fast access to data
and don't have scalability requirements. On the other hand, a distributed cache is
preferable when you need to store larger amounts of data and need horizontal scalability
and high availability.
Optimize Data
Access
•Efficientdata access is crucial for
application performance. Many
applications heavily depend on databases,
which can lead to slow load times if not
optimized. To enhance efficiency:
• Reduce the number of HTTP calls and
network round trips.
• Retrieve data in fewer server calls,
minimizing data retrieval operations.
• Implement caching for static data to
reduce response load and enhance
application speed.
13
14
Optimize Custom
Code
In addition to data access, optimizing custom
business logic and middleware code can
significantly improve application efficiency. Key
considerations include:
Configure custom code, such as logging and
authentication handlers, for optimal performance.
Avoid long-running custom executions in the
business logic layer or middleware as it can block
requests and slow down the application.
Optimize code to execute long-term tasks
asynchronously to avoid disrupting other
processes.
15
Minimize Allocations in Large Objects
Memory management is critical for
high-performance ASP.NET core
applications. Unnecessary object
allocations can impact performance
by forcing the garbage collector to
clean up. To minimize allocations in
large objects:
Cache frequently used large objects
to avoid costly allocations.
Utilize the arraypool to manage
large arrays for buffer pooling.
Avoid assigning short-lived large
objects to hot code paths.
16
Use exceptions
wisely
Exceptions should be rare in your application.
Throwing and handling exceptions can be time-
consuming compared to other code flow
patterns.
Use exceptions only when necessary, and
leverage app diagnostic tools like application
insights to identify and understand exceptions in
your application effectively.
Minimize
exceptions
Recommendations:
D o n o t use throwing or catching exceptions as a
means of normal program flow, especially in hot code
paths.
D o include logic in the app to detect and handle
conditions that would cause an exception.
D o throw or catch exceptions for unusual or
unexpected conditions.
Excellent Tips to Improve 18
Performance
Prefer readformasync over request form
Do not store ihttpcontextaccessor.Httpcontext in a field
Do not open httpcontext through multiple threads. Since it’s not
secure and may cause undefined behavior. Like crashes, hangs, or
data corruption.
Do not record services that are injected into controllers via
background threads.
Do not alter the headers or status code after the http response
body is started. Because ASP.NET core doesn’t buffer the HTTP
response body.
Return large collections
across multiple smaller
pages
A webpage shouldn't load large amounts of data all at once.
When returning A collection of objects, consider whether it
could lead to performance issues. Determine if the design
could produce the following poor outcomes:
Outofmemoryexception or high memory consumption
Slow response times
Frequent garbage collection
Do add pagination to mitigate the preceding scenarios. Using
page size and page index parameters, developers should
favor the design of returning A partial result. When an
exhaustive result is required, pagination should be used to
asynchronously populate batches of results to avoid locking
server resources.
CLIENT-SIDE
IMPROVEMENTS
20
21
Bundling and Minification
Optimize your content before loading it using minification. Make
sure to reduce the code and load all the client-side assets. After you
have reduced the files, put them together to load faster.
Load JavaScript at Last
Be sure that you load JavaScript files last. With this method, your
website will be completed in a short amount of time. Also, when
JavaScript is running, DOM elements are already there. Therefore,
your application can be enhanced speedier.
Shrink Images 22
Large images require a lot of effort to load.
Instead, you can reduce the images with
compression tools to reduce the loading
speed.
Use CDN (Content Delivery Network) in the
context of Angular is a network of servers that
deliver web content, including Angular
libraries and frameworks, to end-users.
If you’re using third-party libraries to CSS
and JavaScript that comes with a CDN version.
You should try to use your CDN file path
instead of downloading the library files. CDN
has multiple servers in different zones. If
you’re using a site within a single region, it will
download the CDN file through the server.
When accessing the site, which is quicker than
self-loading the large library file.
DO CALL ALL DATA DO NOT RETRIEVE DO CONSIDER CACHING DO MINIMIZE NETWORK DO USE
ACCESS APIS MORE DATA THAN IS FREQUENTLY ACCESSED ROUND TRIPS. THE GOAL NO-TRACKING QUERIES
ASYNCHRONOUSLY. NECESSARY. WRITE DATA RETRIEVED FROM A IS TO RETRIEVE THE IN ENTITY FRAMEWORK
QUERIES TO RETURN DATABASE OR REMOTE REQUIRED DATA IN A CORE WHEN ACCESSING
JUST THE DATA THAT'S SERVICE IF SLIGHTLY SINGLE CALL RATHER DATA FOR READ-ONLY
NECESSARY FOR THE OUT-OF-DATE DATA IS THAN SEVERAL CALLS. PURPOSES. EF CORE CAN
CURRENT HTTP ACCEPTABLE. RETURN THE RESULTS OF
REQUEST. DEPENDING ON THE NO-TRACKING QUERIES
SCENARIO, USE A MORE EFFICIENTLY.
MEMORYCACHE OR A
DISTRIBUTEDCACHE.
FOR MORE
INFORMATION,
Do filter and aggregate LINQ queries (with .Where, .Select, or .Sum statements, for
example) so that the filtering is performed by the database.
Do consider that EF Core resolves some query operators on the client, which may lead
to inefficient query execution.
Do not use projection queries on collections, which can result in executing "N + 1" SQL
queries.
We recommend measuring the impact of the preceding high-performance
approaches before committing the code base. The additional complexity of
compiled queries may not justify the performance improvement.
Query issues can be detected by reviewing the time spent accessing data with Application
Insights or with profiling tools. Most databases also make statistics available concerning
frequently executed queries.
🠶Performance and reliability
🠶 T h e following sections provide performance
tips and known reliability problems and
solutions.
Do not store
IHttpContextAccessor.HttpCo
ntext in a field
🠶 D o not do this: The
following example stores
the HttpContext in a field
and then attempts to use it
later.
🠶 T h e preceding code
frequently captures a null
or incorrect HttpContext in
the constructor.
🠶 D o this: The following
example:
🠶Stores
the IHttpContextAccessor in a
field.
🠶Uses the HttpContext field
at the correct time and
checks for null.
Do not use the
HttpContext after
the request is
complete
🠶HttpContext is only valid as long as
there is an active HTTP request in the
ASP.NET Core pipeline. The entire ASP.NET
Core pipeline is an asynchronous chain of
delegates that executes every request.
When the Task returned from this chain
completes, the HttpContext is recycled.
🠶 D o not do this: The following
example uses async void which makes
the HTTP request complete when the
first await is reached:
🠶Which is ALWAYS a bad practice
in ASP.NET Core apps.
🠶Accesses the HttpResponse after
the HTTP request is complete.
🠶Crashes the process.
🠶 D o this: The following
example returns a Task to the
framework, so the HTTP
request doesn't complete until
the action completes.
Do not capture the
HttpContext in
background
threads
🠶 D o not do this: The
following example shows a
closure is capturing
the HttpContext from
the Controller property. This is
a bad practice because the
work item could:
🠶 Ru n outside of the
request scope.
🠶Attempt to read
the wrong
HttpContext.
🠶 D o this: The
following example:
🠶Copies the data required
in the background task
during the request.
🠶Doesn't reference
anything from the controller.
Do not capture services
injected into the
controllers on background
threads
🠶 D o not do this: The
following example shows a
closure is capturing the
DbContext from the Controller
action parameter. This is a
bad practice. The work item
could run outside of the
request scope.
The ContosoDbContext
is scoped to the
request, resulting in
an
ObjectDisposedExceptio
n.
🠶 D o this: The following
example:
🠶 Injects an IServiceScopeFactory
in order to create a scope in the
background work item.
IServiceScopeFactory is a singleton.
🠶 Crea te s a new dependency
injection scope in the background
thread.
🠶 Doesn't reference anything
from the controller.
🠶 Doesn't capture the
ContosoDbContext from the
incoming request.
Thank you