Nugget 21: Preventing SqlClient.SqlException Transient Failure In a Blazor Application

 


In WORKS Commons, a QuickGrid loading job applications provides a search function for users to filter job applications by Company Name. The search is pushed by EF to the server. However the way this was implemented had a fatal flaw: With every character typed by the user, a request is sent to the server. This causes conflicts or interruptions on MSSQL Server, which in turn cases EF execution to abort the batch and an exception is thrown:

2026-10-19 16:13:30.746 -07:00 [WRN] Unhandled exception rendering component: An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure' to the 'UseSqlServer' call.
System.InvalidOperationException: An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure' to the 'UseSqlServer' call.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): The request failed to run because the batch is aborted, this can be caused by abort signal sent from client, or another request is running in the same session, which makes the session busy.
Operation cancelled by user.
at Microsoft.Data.SqlClient.SqlCommand.<>c.b__195_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
ClientConnectionId:4e918cb2-5ca9-45d6-887a-20a5fe657a1a

See issue 133 on GitHub.

The suggestion on the stack trace is important, and it will add resilience, however, it addresses a different problem; it does not really solve the root cause because the grid continues to place requests constantly every time the user types a character. With EnableRetryOnFailure enabled, transient failures should be retried but it does not address the reason. The real solution is to debounce the user input.

This method was added to the View Model and handles the input of the search input control:


The key is in OnCompanySearchChanged() function. What is it doing?

Every single time the user types a new character OnCompanySerchChange() fires and the very first thing it does is cancel the cancellation token. This will kill the previous Task.Delay that was currently waiting.

If a previous delay is interrupted, it throws a TaskCanceledException. The catch, quietly handled it and logs it, preventing setting the value of CompanySearch, which is used to build the query.

If the user stops writing for 300 milliseconds the Task.Delay completes, CompanyName is updated, triggering the actual search logic.

The correction for this defect included other improvements. but debouncing user input was the key change by addressing excessive search requests. The logs tells us that story.

It is worth to notice that debouncing DOES NOT cancel a query that has already started, but it prevents the barrage of searches caused by the user typing. It is also important to mention that the stack trace suggestion was not ignored, hence the other improvements mentioned above; EnableRetryOnFailure() remains useful for eligible transient failures; but this addresses a different problem.


As a final note, WORKS Commons uses MSSQL Server 2025 (RTM) - Latest Update (X64)

Comments

Popular posts from this blog

Nugget 11: Adding Filtering to a Blazor QuickGrid

Nugget 9: Building a JobBank App using Blazor. A Nugget with a Story.

Nugget 14: Connection to MongoDB Atlas - SSL Alert 80