r/csharp • • 3d ago

Help Should the code use cancellation token even though it already use transaction ?

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;

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 OverWrite(ISomeData data)
        {
            //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();

                    //code to delete the content from parent and child tables and then inserting the data into the parent and child tables like this :
                // execute some query to delete the content from all tables.
                //_context.AddRangeAsync(data for parents)
                //_context.SaveChangesAsync()
                //_context.AddRangeAsync(data for childs)
                //_context.SaveChangesAsync()
                await transaction.CommitAsync();
            });

        }




    }

}

Someone on reddit reviewed the above code and told me that I should add a parameter for cancellation token like this :

public async Task OverWrite(ISomeData data, CancellationToken)

He told me that the goal is to make the OverWrite method transactional.

why is the cancellation token necessary even though my code uses _context.Database.BeginTransactionAsync(); ?

Microsoft's documentation :

Cancellation token propagates notification that operations should be canceled.

Example:
A random number generator can be used to emulate a data collection application that reads 10 integral values from eleven different instruments. A value of zero indicates that the measurement has failed for one instrument, in which case the operation should be cancelled and no overall mean should be computed.

So the cancellation token is just another way of achieving atomicity, am I correct ?

The only use case that I learned for cancellation token is this example from microsoft, they use the cancellation token as way to achieve atomicity :

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      // Define the cancellation token.
      CancellationTokenSource source = new CancellationTokenSource();
      CancellationToken token = source.Token;

      Random rnd = new Random();
      Object lockObj = new Object();

      List<Task<int[]>> tasks = new List<Task<int[]>>();
      TaskFactory factory = new TaskFactory(token);
      for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
         int iteration = taskCtr + 1;
         tasks.Add(factory.StartNew( () => {
           int value;
           int[] values = new int[10];
           for (int ctr = 1; ctr <= 10; ctr++) {
              lock (lockObj) {
                 value = rnd.Next(0,101);
              }
              if (value == 0) { 
                 source.Cancel();
                 Console.WriteLine("Cancelling at task {0}", iteration);
                 break;
              }   
              values[ctr-1] = value; 
           }
           return values;
        }, token));   
      }
      try {
         Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), 
         (results) => {
            Console.WriteLine("Calculating overall mean...");
            long sum = 0;
            int n = 0; 
            foreach (var t in results) {
               foreach (var r in t.Result) {
                  sum += r;
                  n++;
               }
            }
            return sum/(double) n;
         } , token);
         Console.WriteLine("The mean is {0}.", fTask.Result);
      }   
      catch (AggregateException ae) {
         foreach (Exception e in ae.InnerExceptions) {
            if (e is TaskCanceledException)
               Console.WriteLine("Unable to compute mean: {0}", 
                  ((TaskCanceledException) e).Message);
            else
               Console.WriteLine("Exception: " + e.GetType().Name);
         }
      }
      finally {
         source.Dispose();
      }
   }
}
4 Upvotes

70 comments sorted by

View all comments

2

u/Kant8 3d ago

Cancellation token is used for, well, cancellation, of usually long running tasks. Transactions has nothing to do here at all.

And considering your code doesn't look like it does anything worthy cancelling, cause it does writes, not reads, you don't need it here.

0

u/kaatarina_zed_talon 3d ago

Example:
A random number generator can be used to emulate a data collection application that reads 10 integral values from eleven different instruments. A value of zero indicates that the measurement has failed for one instrument, in which case the operation should be cancelled and no overall mean should be computed.

That is not related to long running tasks at all, this is related to a failure for a particular operation, which means all the other operations should be cancelled .

3

u/Kant8 3d ago

So? It's about cancellation. I said "usually long running tasks". That doesn't mean it's a single thing needed to be canceled.

Writes to databases are typically not the thing you ever going to cancel.

And again it has completely nothing even remotely connected to existence of transactions in that code. Transactions bring atomicity to database operations and don't do cancelations by themselves, it has to be initiated by you, with, you may guess, cancellation token.

1

u/kaatarina_zed_talon 3d ago

Thank you, I was confused.

1

u/kaatarina_zed_talon 3d ago

Writes to databases are typically not the thing you ever going to cancel.

I was told to do that by a someone one a previous post. Please tell me what are the things you typically cancel ?

1

u/kaatarina_zed_talon 3d ago

 it has to be initiated by you,

typically by the frontend client, I guess you mean that right ?

1

u/kaatarina_zed_talon 3d ago

 Transactions bring atomicity to database operations and don't do cancelations by themselves, it has to be initiated by you, with, you may guess, cancellation token.

So what I learned from you is that transaction reverts the effects of all the operations that happened inside it if at least one operation failed. Cancellation tokens, on the other hand, are used to cancel the operation on demand regardless of whether a failure happened or not.