Skip to content

.NET

Performance Optimisation in ASP.NET Core & SQL Server

Performance problems are often blamed on the framework when the real bottleneck is somewhere else. Effective optimisation starts with measurement, database behaviour and understanding the complete request path.

Performance work is most effective when it begins with measurement rather than assumptions.

I’ve worked on production systems where meaningful performance improvements came not from replacing the technology stack, but from understanding where the application was actually spending its time.

In one large business application, performance optimisation and database indexing contributed to a 60% reduction in system response time.

The experience reinforced an important principle:

Don’t optimise what you haven’t measured.

Start With the Request

When an ASP.NET application feels slow, I start by looking at the complete request path.

A request may involve:

  • middleware
  • authentication
  • business logic
  • database queries
  • external APIs
  • file operations
  • serialization
  • network latency
  • frontend requests

Any of those can become the bottleneck.

Changing application code before identifying where the time is being spent can create complexity without improving performance.

The Database Is Often the First Place to Look

Business applications frequently spend a significant amount of their request time waiting for the database.

SQL Server performance can be affected by:

  • missing indexes
  • poor query patterns
  • large result sets
  • unnecessary joins
  • repeated queries
  • blocking
  • incorrect data types
  • inefficient pagination
  • and queries that retrieve far more information than the application actually needs.

A small improvement in a frequently executed query can sometimes have a larger impact than a significant application-level refactor.

Indexes Need to Support Real Queries

Indexes are powerful, but simply adding more indexes isn’t a performance strategy.

An index should support the way the application actually queries the data.

That means examining:

  • WHERE conditions
  • JOIN columns
  • sorting
  • grouping
  • query frequency
  • selectivity
  • execution plans

Indexes also have a cost.

They consume storage and can increase the cost of writes.

The objective is not maximum indexing.

It’s appropriate indexing.

Avoid Loading Data You Don’t Need

ORMs make data access productive, but convenience can make inefficient queries easy to write.

If a page needs five fields, loading a complete object graph containing dozens of columns and relationships is unnecessary.

Projection can make a significant difference.

Retrieve the data required for the operation rather than automatically loading everything associated with an entity.

Watch for N+1 Queries

N+1 query problems can be easy to miss during development because they may perform adequately with small datasets.

As data grows, they become expensive.

A page that appears to execute one operation may actually generate dozens or hundreds of database queries.

SQL profiling and application telemetry can make these patterns visible.

Once identified, the data-access strategy can usually be improved significantly.

Pagination Matters

Large datasets should rarely be loaded in full simply to display a small portion of them.

Pagination reduces:

  • database work
  • network transfer
  • application memory usage
  • serialization cost
  • browser rendering

But pagination also needs to be designed carefully.

As datasets grow, different pagination strategies can behave very differently.

Async Doesn’t Automatically Mean Fast

ASP.NET Core makes asynchronous programming straightforward, particularly for I/O-bound work.

Async operations can improve scalability by avoiding blocked threads while waiting for databases, APIs or other external resources.

But adding async everywhere doesn’t fix an inefficient query or a slow external dependency.

Concurrency and performance are related, but they aren’t the same thing.

External APIs Need Boundaries

A page that depends synchronously on several external services inherits the latency and reliability of every one of them.

Depending on the application, useful strategies can include:

  • timeouts
  • caching
  • background processing
  • retry policies
  • circuit-breaking
  • queue-based workflows

The right approach depends on how current the information needs to be and what should happen when the external system is unavailable.

Cache the Right Things

Caching can dramatically improve performance when applied to data that is:

  • expensive to calculate
  • frequently requested
  • relatively stable

But caching everything introduces its own problems.

The difficult part of caching isn’t storing data.

It’s deciding when that data is no longer valid.

A caching strategy therefore needs an invalidation strategy.

Measure Again

After making an optimisation, measure the system again.

  • Did response time actually improve?
  • Did database load decrease?
  • Did another bottleneck become visible?
  • Did memory consumption increase?
  • Did the optimisation affect correctness?

Performance work should be iterative:

  1. measure
  2. identify
  3. change
  4. verify
  5. repeat

Performance Is a System Property

ASP.NET Core is only one part of application performance.

SQL Server is only one part.

Real performance emerges from the interaction between:

  • application code
  • database design
  • network behaviour
  • external services
  • infrastructure
  • caching
  • frontend behaviour
  • and actual usage patterns.

The most effective optimisation work comes from understanding that complete system rather than trying to make one layer individually fast.

MoreThoughts

Have a project in mind?

Let's buildSomethingThat works.

I'm available for selected .NET, nopCommerce, eCommerce and software-development projects, as well as technical consulting and long-term collaboration.

Fields marked * are required.