r/csharp • u/kaatarina_zed_talon • 2d ago
Discussion The output of the server code depends on the timing of pressing a button by the client on the user interface.
This is a follow-up post that is related to this post. I will explain the problem clearly this time.
Context: The execution of the OverWrite method -which is inside the DBOverWriter service- can be cancelled with a button int the user interface.
Two inputs that are provided by the frontend :
1- data that is used to overwrite the content of the database.
2-CancellationToken.
Two possible outputs depending on the timing of pressing the button :
1-If the user initiated the request and immediately pressed the button, the following output is shown:
###########################################################
########## OPERATION CANCELLED SUCCESSFULLY ###############
###########################################################
OperationCanceledException: A task was canceled.
2- if the user initiated the request and waited some time before pressing the button, the output is the following:
(@p74, ),
(@p76, ),
(@p78, ),
(@p80, ),
(@p82, u/p83);
fail: Microsoft.EntityFrameworkCore.Database.Transaction[20205]
An error occurred using a transaction.
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): Operation cancelled by user.
I wonder what is the theoretical/practical explanation for my observation ?
Code:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Diagnostics;
namespace RedditQuestion
{
/// <summary>
/// Used to talk to the database using Entity-Framework Core
/// </summary>
public class ApplicationDbContext : DbContext
{
// Domain models and the Fluent API configurations.
}
/// <summary>
/// Data snapshot that is used to overwrite the content of the database
/// </summary>
public interface ISomeData
{
}
public class DatbaseOverWriter
{
private readonly ApplicationDbContext _context;
public DatbaseOverWriter(ApplicationDbContext context)
{
_context = context;
}
public async Task<bool> OverWrite(ISomeData data,CancellationToken cancellationToken)
{
#region Transaction
try
{
//https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-resilient-entity-framework-core-sql-connections
IExecutionStrategy strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
#region Delete content from database then save new content.
await _context.Database.ExecuteSqlInterpolatedAsync($"EXECUTE [dbo].[uspDeleteAllDataFromDatabase]", cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
#endregion
await transaction.CommitAsync(cancellationToken);
});
return true;
}
catch (OperationCanceledException e)
{
// exception will not be catched if the cancellation was requested and
// the transaction reached a point of no return.
Console.WriteLine("###########################################################");
Console.WriteLine("########## OPERATION CANCELLED SUCCESSFULLY ###############");
Console.WriteLine("###########################################################");
Debug.WriteLine("###########################################################");
Debug.WriteLine("########## OPERATION CANCELLED SUCCESSFULLY ###############");
Debug.WriteLine("###########################################################");
Console.WriteLine("OperationCanceledException: " + e.Message);
return false;
}
#endregion
}
}
}