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>
{
}
Related
I created a biometric authentication service that starts an activity and registers a callback to a static EventHandler for the result.
The handler:
public class BiometricHandler : IBiometricHandler
{
private TaskCompletionSource<byte[]> taskCompletionSource;
public Task<byte[]> StartBiometricAuth()
{
Intent intent = new Intent(Android.App.Application.Context, typeof(BiometricActivity));
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
taskCompletionSource = new TaskCompletionSource<byte[]>();
BiometricActivity.BiometricEventHandler += BiometricCompleted;
return taskCompletionSource.Task;
}
private void BiometricCompleted(object sender, BiometricEventArgs e)
{
taskCompletionSource.SetResult(e.Success ? e.Payload : new byte[] { });
BiometricActivity.BiometricEventHandler -= BiometricCompleted;
}
}
And the activity (not the actual code obviously):
public class BiometricActivity : Activity
{
public static event EventHandler<BiometricEventArgs> BiometricEventHandler;
private readonly int BIOMETRIC_REQUEST = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Intent intent = new Intent(Application.Context, typeof(BiometricAuth));
StartActivityForResult(intent, BIOMETRIC_REQUEST);
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
BiometricEventHandler?.Invoke(
this,
new BiometricEventArgs(
true, new byte[] {1, 2, 3}
);
Finish();
}
}
The above code throws a NullReferenceException:
0xFFFFFFFFFFFFFFFF in System.Diagnostics.Debugger.Mono_UnhandledException_internal
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.Diagnostics/Debugger.cs:125,4
0x20 in Android.Runtime.DynamicMethodNameCounter.57
0x6 in Xamarin.Forms.Platform.Android.PlatformConfigurationExtensions.OnThisPlatform<Xamarin.Forms.Application> at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\PlatformConfigurationExtensions.cs:8,4
0xC in Xamarin.Forms.Platform.Android.AppCompat.FragmentContainer.OnResume at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\AppCompat\FragmentContainer.cs:126,4
0x8 in AndroidX.Fragment.App.Fragment.n_OnResume at C:\a\_work\1\s\generated\androidx.fragment.fragment\obj\Release\monoandroid12.0\generated\src\AndroidX.Fragment.App.Fragment.cs:2570,4
0x11 in Android.Runtime.DynamicMethodNameCounter.57
And the console:
**System.NullReferenceException:** 'Object reference not set to an instance of an object.'
I narrowed it down to the Finish() method.
There is no exception when it is not called.
I tried calling Finish() from the handler using BroadcastReceiver, same result.
Why does Finish() throw this exception?
Well, it seems like the issue is an asynchronous race condition.
Finish() takes a bit longer to complete than the service to return the data.
Then the content page tries to perform a UI event, and since Finish() hasn't finished yet, the activity is still "visible" and the content page is not yet responsible for the UI thread. So the UI event fails with the exception.
Since there doesn't seem to be an easy way to await the Finish() call, I used Task.Await to delay the UI event for a bit and to Finish() complete.
Not ideal, but at least NullReferenceException is gone
My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}
I have a custom ModelBinder, which, when I load test my app, and run Ants profiler on, identified reading the Request.Content as string as a hotspot:
public class QueryModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var body = actionContext.Request.Content.ReadAsStringAsync().Result;
Is there a more efficient way of doing this?
Or am I reading the ANTS profiler incorrectly?
How big is the content? Note that you might be seeing a lot of time because you are calling this network call in sync rather than async.
You can potentially read the string earlier async and stash it in the request property.
Alternatively you can write a formatter instead, and then decorate your parameter with [FromBody].
The recommended approach here is to use a FromBody and a formatter, since it naturally fits with the WebAPI architecture:
For that you would write a media type formatter:
public class StringFormatter : MediaTypeFormatter
{
public StringFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/mystring"));
}
public override bool CanReadType(Type type)
{
return (type == typeof (string));
}
public override bool CanWriteType(Type type)
{
return false;
}
public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger,
CancellationToken cancellationToken)
{
if (!CanReadType(type))
{
throw new InvalidOperationException();
}
return await content.ReadAsStringAsync();
}
}
Register it in webapiconfig.cs
config.Formatters.Add(new StringFormatter());
And consume in an action
public string Get([FromBody]string myString)
{
return myString;
}
The other design (not as recommended because of coupling between the filter and the binder):
Implement a model binder (this is super Naive):
public class MyStringModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
// this is a Naive comparison of media type
if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
{
bindingContext.Model = actionContext.Request.Properties["MyString"] as string;
return true;
}
return false;
}
}
Add an authroization filter (they run ahead of modelbinding), to you can async access the action. This also works on a delegating handler:
public class MyStringFilter : AuthorizationFilterAttribute
{
public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
{
var myString = await actionContext.Request.Content.ReadAsStringAsync();
actionContext.Request.Properties.Add("MyString", myString);
}
}
}
Register it in WebApi.Config or apply it to the controller:
WebApiConfig.cs
config.Filters.Add(new MyStringFilter());
ValuesController.cs
[MyStringFilter] // this is optional, you can register it globally as well
public class ValuesController : ApiController
{
// specifying the type here is optional, but I'm using it because it avoids having to specify the prefix
public string Get([ModelBinder(typeof(MyStringModelBinder))]string myString = null)
{
return myString;
}
}
(Thanks for #Kiran Challa for looking over my shoulder, and suggesting the Authorization filter)
EDIT: One thing to always remember with relatively large strings (consuming more than 85KB so about 40K Chars) can go into the Large Object heap, which will wreak havoc on your site performance. If you thing this is common enough, break the input down into something like a string builder/array of strings or something similar without contiguous memory. See Why Large Object Heap and why do we care?
I've got a class that requires access to the HttpRequestMessage in my Web API service. At the moment, I've got the following code to capture the message in the pipeline and save it for later (based on this and this):
public class ContextCapturingControllerActivator : IHttpControllerActivator
{
private readonly IKernel kernel;
private HttpRequestMessage requestMessage;
public ContextCapturingControllerActivator(IKernel kernel)
{
this.kernel = kernel;
}
public IHttpController Create(HttpRequestMessage requestMessage,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
this.kernel.Rebind<HttpRequestMessage>()
.ToConstant<HttpRequestMessage>(requestMessage);
var controller = (IHttpController)this.kernel.GetService(controllerType);
this.requestMessage = requestMessage;
requestMessage.RegisterForDispose(
new Release(() => this.kernel.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
In my composition root, I configure the ControllerActivator:
kernel.Bind<IHttpControllerActivator>()
.To<ContextCapturingControllerActivator>();
The end result is that from the perspective of the configuration, the HttpRequestMessage is "magically" injected wherever it is requested since it is done for us inside the ControllerActivator. I have not been able to inject the message from my composition root. I'm also not crazy about the Rebind since it's there to avoid adding a new binding every time the service is called. I suspect it's due to the singleton nature of the Web API stack, but have not been able to sort out how to deal with that properly.
In general, I cannot use the latest unstable Nuget package of Ninject web api due to the error reported (and ignored) here.
Can anyone suggest the proper way to improve my code to make it a bit more clear and make life easier for future maintainers (and let's face it -- that's probably going to be me).
Thanks.
Here is what I did, but I believe it depends on Web API 2.0+.
I created an instance class that wraps the current context's http request:
public class HttpRequestMessageWrapper
{
private readonly HttpRequestMessage m_httpRequestMessage;
public HttpRequestMessageWrapper()
{
m_httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
}
public HttpRequestMessage RequestMessage
{
get
{
return m_httpRequestMessage;
}
}
}
Then I bound the HttpRequestMessage to the property with the ToMethod binding in request scope.
container.Bind<HttpRequestMessage>().ToMethod(ctx => new HttpRequestMessageWrapper().RequestMessage).InRequestScope();
I've tried the method that #Mackers proposed which is the cleanest way.... however, in my specific scenario, it didn't work due to a timing issue. For my case, I needed to inject an object into the apicontroller ctor and that object required the HttpRequestMessage. The HttpContext.Current.Items["MS_HttpRequestMessage"]isn't populated until the controller has been constructed and initialized and I couldn't find any other way to access it. So I resorted to creating a custom DelegatingHandler and rebinding the current request message as they come in.
public class CurrentHttpRequestMessageHandler : DelegatingHandler
{
[SecuritySafeCritical]
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
UpdateScopeWithHttpRequestMessage(request);
return base.SendAsync(request, cancellationToken);
}
internal static void UpdateScopeWithHttpRequestMessage(HttpRequestMessage request)
{
NinjectConfig.GetConfiguredKernel().Rebind<HttpRequestMessage>().ToMethod(ctx => { return request; })
.InRequestScope();
}
}
The GetConfiguredKernel is a static method I created to simply return the static Kernel instance already configured.
public class NinjectConfig
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
private static StandardKernel _kernel;
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
public static IKernel GetConfiguredKernel()
{
if (_kernel != null)
return _kernel;
return CreateKernel();
}
....
Then register the DelegatingHandler with the HttpConfiguration:
config.MessageHandlers.Add(new CurrentHttpRequestMessageHandler());
Building off of Macker's answer, System.Web has an HttpRequestBase class that you can use and simplify unit testing the code. Anywhere in the code that the request is required, specify the HttpRequestBase type as the constructor parameter and register it with the below method:
Ninject example:
Bind<HttpRequestBase>().ToMethod(context => new HttpRequestWrapper(HttpContext.Current.Request));
Unity example:
container.RegisterType<HttpRequestBase>(new InjectionFactory(_ => new HttpRequestWrapper(HttpContext.Current.Request)));
I understand await waits for a task (an awaitable) to complete.
But I'm confused about what that actually means.
The code that doesn't work:
public async override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response.Content != null)
{
var responseContent = await actionExecutedContext.Response.Content.ReadAsStringAsync();
DoSomething(responseContent);
}
}
The code that does work:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response.Content != null)
{
var responseContent = actionExecutedContext.Response.Content.ReadAsStringAsync().ContinueWith(
task =>
{
DoSomething(task.Result);
});
}
}
Obviously the error message An asynchronous module or handler completed while an asynchronous operation was still pending. tells me that there was no waiting for the async call to complete but instead the "main" thread continued. I expected the thread to continue but not within the current method. I thought the thread would return to the asp.net stack do some other work and return once the await asyncOperation() operation completed.
I'm using await in other places too - (e.g. waiting for web service responses) - and I didn't run into similar problems anywhere. I wonder why the IActionFilterAttribute behaves differently. In fact my web service calls probably take way longer than reading the content of the response into a string.
Can someone please enlighten me? I have the feeling I didn't understand the concept.
Adding async code to a method that returns void is dangerous and almost never what you actually want to do. See What's the difference between returning void and returning a Task?.
Instead, you need to override/implement a method that returns a task. In this case, ActionFilterAttribute hides the Task that IHttpActionFilter provides, so you'll need to implement IActionFilter (ExecuteActionFilterAsync) instead. If you want to use you code as an attribute, just make sure you also derive from the Attribute class.
For example:
public class AsyncActionFilterAttribute : Attribute, IActionFilter
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
HttpResponseMessage response = await continuation();
DoSomething(response);
return response;
}
}
Instead of implementing
public async override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
you have to implement the async version of OnActionExecuted method as follows:
public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
This way you can use await inside a method and behavior will be as you expected.
Hope this helps.