Вот реализация моего класса хранилища AsyncLocal.
Код: Выделить всё
public class AsyncLocalStorage : ILocalStorage
{
private readonly AsyncLocal _context;
public AsyncLocalStorage()
{
_context = new AsyncLocal(OnValueChanged);
}
private static void OnValueChanged(AsyncLocalValueChangedArgs args)
{
Log("OnValueChanged! Prev: {0} ; Current: {1}", RuntimeHelpers.GetHashCode(args.PreviousValue), RuntimeHelpers.GetHashCode(args.CurrentValue));
}
public T GetData()
{
try
{
return _context.Value;
}
catch (Exception ex)
{
Log("Ex: " + ex.ToString());
}
return default(T);
}
public void SetData(T value)
{
_context.Value = value;
}
public void Clear()
{
_context.Value = default(T);
}
}
Код: Выделить всё
02-05-2024 20:10:38 - [4:(DEBUG)] - [TName: ]TRANSACTION STARTED (The AsyncLocal Object is created and I assign my object "Transaction" using context.SetData())
02-05-2024 20:10:38 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]The async SQL call is made.... (The Transaction object is modified context.GetData() . I guess the obj is passed to thread 9 )
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]---- random things are taken care off using the Transaction object that resulted in the following OnValueChanged logs .
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]The Async SQL call returned a Task object .
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]Processing of the metadata done on thread 4 is over.
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]Most of the processing on Thread 9 is also over and The Task returned by SQL call has finished.
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]Now the Transaction object is fetched with context.GetData() and is Loaded into a Thread Processor that runs independently using "ThreadPool.QueueWorkItem(Event, transaction) , which happens to be thread 10"
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]Everything seems to be over and its time to call context.Clear() //called.
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]Tried getting the transaction object here to verify using context.GetData(), returned null here.
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ProcessThread]Working with the transaction object reference in the Event thread.
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ProcessThread]Also working with the transaction object that is passed to this thread processor. Have no idea why the below log has occured.
02-05-2024 20:10:39 - [1:(ERROR)] - OnValueChanged! Prev: 5773521 ; Current: 0 (I am sure the transaction object goes out of scope here)
02-05-2024 20:10:39 - [4:(DEBUG)] - [TName: ]TRANSACTION STARTED (new repetition, calling the GetData function again before creating a new transaction object and assigning. I only assign a new object if it is null)
02-05-2024 20:10:40 - [1:(ERROR)] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:40 - [4:(DEBUG)] - [TName: ] Suprise suprise!! it is not NULL. I have no idea why it isn't .
02-05-2024 20:10:40 - [4:(DEBUG)] - [TName: ]I have already cleared all the component objects in the transaction object . A New transaction object is needed here.
РЕДАКТИРОВАТЬ: Минимальный воспроизводимый пример приведен ниже. Я постарался сделать его маленьким
Код: Выделить всё
public class Worker : BackgroundService
{
private readonly ILogger _logger;
private readonly BackgroundTask _backgroundtask;
public Worker(ILogger logger, BackgroundTask task)
{
_logger = logger;
_backgroundtask = task;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_backgroundtask.MakeTask();
await Task.Delay(1000, stoppingToken);
}
}
}
public sealed class BackgroundTask
{
private readonly ILogger _logger;
public BackgroundTask(ILogger logger)
{
_logger = logger;
}
public void MakeTask()
{
Transactions trans = TransactionService.GetCurrentTransaction();
if(trans == null)
{
Console.WriteLine("[T:] There is no TransactionObject", Thread.CurrentThread.ManagedThreadId);
trans = TransactionService.GetOrCreateTransaction();
}
else
{
Console.WriteLine("[T:] There is a Transaction Object", Thread.CurrentThread.ManagedThreadId);
}
trans.PrintTransactionName();
Task.Run(() =>
{
trans.SetTransactionName("transaction2");
trans.PrintTransactionName();
trans.ClearEverything();
});
}
}
public class TransactionService
{
private static AsyncLocalStorage transactionContext;
static TransactionService()
{
transactionContext = new AsyncLocalStorage();
}
public static Transactions GetCurrentTransaction()
{
return transactionContext.GetData();
}
public static Transactions GetOrCreateTransaction()
{
var transaction = GetCurrentTransaction();
if (transaction == null)
{
transaction = new Transactions("Transaction1");
transactionContext.SetData(transaction);
}
return transaction;
}
public static void RemoveOutstandingTransactions()
{
transactionContext.Clear();
}
}
public class Transactions
{
private string transactionName;
public Transactions(string trans)
{
transactionName = trans;
}
public void SetTransactionName(string trans)
{
transactionName = trans;
}
public void PrintTransactionName()
{
if (!string.IsNullOrEmpty(transactionName))
{
Console.WriteLine("[T:] The transactionName is {1}", Thread.CurrentThread.ManagedThreadId, transactionName);
}
}
public void ClearEverything()
{
transactionName = null;
Console.WriteLine("[T:] The Transaction Object is being cleared", Thread.CurrentThread.ManagedThreadId);
TransactionService.RemoveOutstandingTransactions();
// Just checking if the object is cleared in this thread.
if(TransactionService.GetCurrentTransaction() == null)
{
Console.WriteLine("[T:] The Transaction Object is null after clearing", Thread.CurrentThread.ManagedThreadId);
}
}
}
Почему в начале следующего повторения после очистки объекта AsyncLocal появляется объект транзакции. Журналы будут выглядеть примерно так...
Код: Выделить всё
[T:] There is no TransactionObject
[T:] OnValueChanged! Prev: 0 ; Current: 72766
[T:] The transactionName is Transaction1
[T:] OnValueChanged! Prev: 0 ; Current: 72766
[T:] The transactionName is transaction2
[T:] OnValueChanged! Prev: 72766 ; Current: 0
[T:] The Transaction Object is being cleared
[T:] OnValueChanged! Prev: 72766 ; Current: 0
[T:] The Transaction Object is null after clearing
[T:] There is a Transaction Object
[T:] OnValueChanged! Prev: 72766 ; Current: 0
[T:] OnValueChanged! Prev: 0 ; Current: 72766
[T:] The transactionName is transaction2
[T:] The Transaction Object is being cleared
[T:] OnValueChanged! Prev: 72766 ; Current: 0
[T:] The Transaction Object is null after clearing
[T:] OnValueChanged! Prev: 0 ; Current: 72766
Подробнее здесь: https://stackoverflow.com/questions/784 ... emicrosoft