AsyncTaskCodeActivity fails when the context parameter is accessed after the first await is performed. For example:
public class TestAsyncTaskCodeActivity : AsyncTaskCodeActivity<int>
{
protected async override Task<int> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
{
await Task.Delay(50);
// context has already been disposed and the next line throws
// ObjectDisposedException with the message:
// An ActivityContext can only be accessed within the scope of the function it was passed into.
context.Track(new CustomTrackingRecord("test"));
// more awaits can happen here
return 3;
}
}
Is there any simple way to preserve the context so it can be used also after awaiting something?
Ah.
When I wrote AsyncTaskCodeActivity<T>
, I assumed that the AsyncCodeActivityContext
was in fact going to be the same instance at the beginning and end of the asynchronous method, and be available all the way through. This is not the case (which is a bit odd - not sure why the WF team made that decision).
Instead, the AsyncCodeActivityContext
can only be accessed at the beginning and end of the activity. Awkward, indeed.
The updated code below will allow you to access the context at the beginning (e.g., reading In variables) and then access the context again at the end. I also introduce an optional TState
, which can be used for storing activity state (which the activity can access throughout its execution). Let me know if this fits your needs; I haven't tested it.
public abstract class AsyncTaskCodeActivity<T, TState> : AsyncCodeActivity<T>
{
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
TState activityState = PreExecute(context);
context.UserState = activityState;
var task = ExecuteAsync(activityState);
return AsyncFactory<T>.ToBegin(task, callback, state);
}
protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult asyncResult)
{
var result = AsyncFactory<T>.ToEnd(asyncResult);
return PostExecute(context, (TState)context.UserState, result);
}
protected virtual TState PreExecute(AsyncCodeActivityContext context)
{
return default(TState);
}
protected abstract Task<T> ExecuteAsync(TState activityState);
protected virtual T PostExecute(AsyncCodeActivityContext context, TState activityState, T result)
{
return result;
}
}
public abstract class AsyncTaskCodeActivity<T> : AsyncTaskCodeActivity<T, object>
{
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With