I have a Background Service that performs some tasks and jobs on a long running thread. For simplicity, We’ll consider this as a scheduler that makes an async SQL call and repeats. Now I use AsyncLocal to store metadata for logging purpose on the service and I want to group each repetition of the task as a transaction. I figured that unlike web applications the BackgroundService will have a single AsyncLocal(ExecutionContext) object that I have to clear after every repetition (every SQL call in this example). Coming to the problem, I cannot seem to clean up the object .
Here is the implementation of my AsyncLocal storage class.
public class AsyncLocalStorage<T> : ILocalStorage<T>
{
private readonly AsyncLocal<T> _context;
public AsyncLocalStorage()
{
_context = new AsyncLocal<T>(OnValueChanged);
}
private static void OnValueChanged(AsyncLocalValueChangedArgs<T> 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);
}
}
Here , I set my metadata to the AsyncLocal object and then I call the Clear function . But the object instance remains . I have attached logs for further reference.
02-05-2024 20:10:38 - [4:(DEBUG)<t:4>] - [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)<t:4>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)<t:4>] - [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)<t:9>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)<t:4>] - [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)<t:5>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [1:(ERROR)<t:9>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [1:(ERROR)<t:5>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [1:(ERROR)<t:5>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [1:(ERROR)<t:5>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)<t:4>] - [TName: ]The Async SQL call returned a Task object .
02-05-2024 20:10:39 - [1:(ERROR)<t:9>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)<t:4>] - [TName: ]Processing of the metadata done on thread 4 is over.
02-05-2024 20:10:39 - [1:(ERROR)<t:4>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)<t:9>] - [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)<t:9>] - [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)<t:10>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:39 - [4:(DEBUG)<t:9>] - [TName: ]Everything seems to be over and its time to call context.Clear() //called.
02-05-2024 20:10:39 - [1:(ERROR)<t:9>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)<t:9>] - [TName: ]Tried getting the transaction object here to verify using context.GetData(), returned null here.
02-05-2024 20:10:39 - [4:(DEBUG)<t:10>] - [TName: ProcessThread]Working with the transaction object reference in the Event thread.
02-05-2024 20:10:39 - [4:(DEBUG)<t:10>] - [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)<t:10>] - OnValueChanged! Prev: 5773521 ; Current: 0
02-05-2024 20:10:39 - [4:(DEBUG)<t:4>] - [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)<t:4>] - OnValueChanged! Prev: 0 ; Current: 5773521
02-05-2024 20:10:40 - [4:(DEBUG)<t:4>] - [TName: ] Suprise suprise!! it is not NULL. I have no idea why it isn't .
02-05-2024 20:10:40 - [4:(DEBUG)<t:4>] - [TName: ]I have already cleared all the component objects in the transaction object . A New transaction object is needed here.
Why does this happen, Is there something I am missing ?