I'm using the Enterprise Library Transient Fault Handling Application Block in a Windows 8 Store App with the WCF Data Services Client for ODATA. I want to use retry logic for transient errors occurring when calling the ODATA service. I built a custom transient error detection strategy.
I have also built a LoadTaskAsync extension method for the DataServiceCollection as the LoadAsync method does not return a Task (instead the DataServiceCollection raises the LoadCompleted event).
So I can load data into a DataServiceCollection as follows:
var query = this.DataContext.Products.Where(item => item.Modified >= anchor);
var products = new DataServiceCollection<Product>(this.DataContext);
await this.retryPolicy.ExecuteAsync(() => products.LoadTaskAsync(query));
Now the documentation for the Enterprise Library Transient Fault Handling Application Block states that
The taskFunc argument you pass to the ExecuteAsync method is not necessarily invoked in the same synchronization context used when calling ExecuteAsync originally; so if you need to start the task from within the UI thread for example, be sure to schedule it explicitly within the delegate.
I need to invoke the LoadTaskAsync method on the UI thread as the load operation may update products that were already tracked by the data context and that are data bound to the UI.
Question is how? Preferably without modifying the LoadTaskAsync extension method (what if it was not my code to change). I was thinking of creating an extension method for the RetryPolicy that calls the ExecuteAsync method while making sure the taskFunc is invoked on the UI thread.
The easiest way perhaps is to modify the LoadTaskAsync extension method to pass in a TaskCreationOptions.AttachedToParent, so I could create an extension method for the RetryPolicy as follows:
public static Task<TResult> ExecuteCurrentSynchronizationContextAsync<TResult>(
this RetryPolicy retryPolicy,
Func<TaskCreationOptions, Task<TResult>> taskFunc)
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
return
retryPolicy.ExecuteAsync(
() =>
Task.Factory.StartNew(
() => taskFunc(TaskCreationOptions.AttachedToParent),
CancellationToken.None,
TaskCreationOptions.None,
scheduler).Unwrap());
}
Note that taskFunc must now be a Func<TaskCreationOptions, Task<TResult>>.
I would then call this as follows:
await
this.retryPolicy.ExecuteCurrentSynchronizationContextAsync(
creationOptions => products.LoadTaskAsync(query, creationOptions));
As I prefer not to change the LoadTaskAsync extension method, how could I change this RetryPolicy ExecuteCurrentSynchronizationContextAsync extension method so that taskFunc can be a Func<Task<TResult>> again while making sure taskFunc is invoked on the UI thread?
I would not recommend using AttachedToParent with asynchronous tasks. In fact, most promise-style tasks will specify DenyChildAttach which prevents AttachedToParent from working.
Instead, you just need to capture the synchronization context itself and use that. There is a wrinkle: Windows Store apps don't allow synchronous invocation on the synchronization context, so you'll need to either use CoreDispatcher.RunAsync instead of SynchronizationContext, or build your own async-aware extension method for SynchronizationContext. Of those two, I prefer using the SynchronizationContext approach; it's a bit more code in this case, but it means you don't have to tie your code (presumably service-layer code) to this specific UI framework.
So, first we define a RunAsync on SynchronizationContext, which will (asynchronously) execute asynchronous code on a specified synchronization context:
public static Task<TResult> RunAsync<TResult>(this SynchronizationContext context, Func<Task<TResult>> func)
{
var tcs = new TaskCompletionSource<TResult>();
context.Post(async _ =>
{
try
{
tcs.TrySetResult(await func());
}
catch (OperationCanceledException)
{
tcs.TrySetCanceled();
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}, null);
return tcs.Task;
}
Then we can capture and use the SynchronizationContext:
public static Task<TResult> ExecuteOnCurrentSynchronizationContextAsync<TResult>(
this RetryPolicy retryPolicy,
Func<Task<TResult>> taskFunc)
{
var context = SynchronizationContext.Current ?? new SynchronizationContext();
return retryPolicy.ExecuteAsync(() => context.RunAsync(taskFunc));
}
Related
Given:
A legacy non-async API method on an ASP.NET/WCF web service
New async internal library
New async Web API controller that should be used going forward
A "storage provider" object that only has an async interface. Its tests pass when run asynchronously, and when run synchronously outside a request context.
The option "go async all the way" is not on the table, since it would break backward compatibility.
public class Impl {
// This works fine when used asynchronously
public Task<Guid> SaveThingAsync(Thing thingToSave) {
return await _storageProvider.saveAsync(thingToSave);
}
public Guid SaveThing(Thing thingToSave) {
// "Obviously", this code creates a deadlock when called
// from within the request context
// return SaveThingAsync(thingToSave).Result
// Not so obviously, this also creates a deadlock
// return SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult()
// This was deadlocking, but magically stopped
// return Task.Run(
// async () => await SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// ).Result;
// This one works
var saveTask = Task.Run(async () =>
await SaveThingAsync(thingToSave)));
var result = saveTask.ConfigureAwait(false).GetAwaiter().GetResult();
return result;
}
Why?
Task.Run steps "outside" the request context - it just runs on the thread pool context. So, it won't deadlock because SaveThingAsync doesn't resume on the request context.
On a side note, the ConfigureAwait(false) is meaningless there, since there is no await to configure.
On another side note, "async all the way" should still be an option. WebAPI and WCF clients don't care whether the implementation they're calling is synchronous or asynchronous. Changing a WCF method implemented synchronously to a WCF method implemented asynchronously is invisible to client code.
I am writing an ASP.NET MVC 5 application which among others uses web services to get/process some the data.
The data flow of the app is following: MVC Action -> Service B -> ExtSvc which is async wrapper of a web service
Here are some examples:
public class ExtSvc
{
//Convert Event based async pattern to task based async pattern:
private Task<Response> ProcessExtRequestAsync(Request request)
{
TaskCompletionSource<Response> taskCompletionSource =
AsyncServiceClientHelpers.CreateSource<Response>(request);
ProcessRequestCompletedEventHandler handler = null;
handler =
(sender, e) =>
AsyncServiceClientHelpers.TransferCompletion(
taskCompletionSource,
e,
() => e.Result,
() => this.Service.ProcessRequestCompleted -= handler);
this.Service.ProcessRequestCompleted += handler;
try
{
this.Service.ProcessRequestAsync(request, taskCompletionSource);
}
catch (Exception)
{
this.Service.ProcessRequestCompleted -= handler;
taskCompletionSource.TrySetCanceled();
throw;
}
return taskCompletionSource.Task;
}
//Usage:
public async Task<Response> UpdateRequest(some arguments)
{
//Validate arguments and create a Request object
var response = await this.ProcessExtRequestAsync(request)
.ConfigureAwait(false);
return response;
}
}
Class B is the one that uses ExtSvc in a synchronous way
public class B
{
public ExtSvc service {get; set;}
public Response Update(arguments)
{
//some logic
var result = this.ExtSvc.UpdateRequest(arguments).Result;
//some logic
return result
}
}
Finally the MVC action (also synchronous)
public ActionResult GetResponse(int id)
{
//some logic
B.Update(id);
//some logic
return View(...);
}
The described flow throws an error
A first chance exception of type 'System.InvalidOperationException'
occurred in System.Web.dll
Additional information: An asynchronous operation cannot be started at
this time. Asynchronous operations may only be started within an
asynchronous handler or module or during certain events in the Page
lifecycle. If this exception occurred while executing a Page, ensure
that the Page is marked <%# Page Async="true" %>. This exception may
also indicate an attempt to call an "async void" method, which is
generally unsupported within ASP.NET request processing. Instead, the
asynchronous method should return a Task, and the caller should await
it.
on the following line of ExtSvc : this.Service.ProcessRequestAsync(request, taskCompletionSource); ProcessRequestAsync is a void method
So it corresponds to:
This exception may
also indicate an attempt to call an "async void" method, which is
generally unsupported within ASP.NET request processing
I know that converting GetResponse MVC action to asynchronous (by using async/await) and also converting the B class that actually uses ExtSvc to be asynchronous resolves the issue.
BUT my questions is:
If I can't change the signature of B class (because of an interface it implements) to return Task<Response> instead of Response it basically means that I can't use async/await on it so how this issue could be resolved?
ProcessRequestAsync is void but it's not async void. It looks like it's an EBAP API. EBAP components generally use AsyncOperationManager/AsyncOperation, which in turn do use SynchronizationContext to notify the underlying platform of the asynchronous operation (the last link is to my MSDN article on SynchronizationContext).
The exception you're seeing is because ASP.NET sees that notification (of the asynchronous operation starting) and says "whoa, there, fella. You're a synchronous handler! No async for you!"
Hands-down, the best approach is to make all methods asynchronous that should be asynchronous. This means B.Update should be B.UpdateAsync. OK, so there's an interface IB.Update - just change the interface to IB.UpdateAsync too. Then you're async all the way, and the code is clean.
Otherwise, you'll have to consider hacks. You could use Task.Run as #neleus suggested - that's a way of avoiding the ASP.NET SynchronizationContext so it doesn't "see" the asynchronous operation starting - but note that "ambient context" such as HttpContext.Current and page culture is lost. Or, you could (temporarily) install a new SynchronizationContext() onto the request thread - which also avoids the ASP.NET SynchronizationContext while staying on the same thread - but some ASP.NET calls assume the presence of the ASP.NET SynchronizationContext and will fail.
There's another hack you could try; it might work but I've never done it. Just make your handler return a Task<ActionResult> and use Task.FromResult to return the view: return Task.FromResult<ActionResult>(View(...)); This hack will tell ASP.NET that your handler is asynchronous (even though it's not).
Of course, all of these hacks have the primary disadvantage that you're doing sync-over-async (this.ExtSvc.UpdateRequest(arguments).Result), which means you'll be using one extra unnecessary thread for the duration of each request (or two threads, if you use the Task.Run hack). So you will be missing all the benefits of using asynchronous handlers in the first place - namely, scalability.
I think the error occurs because your code
this.Service.ProcessRequestAsync(request, taskCompletionSource);
actually calls SynchronizationContext's OperationStarted method that results in error as described here.
As a possible solution you can call your action on ThreadPoolSynchronizationContext
public async Task<ActionResult> GetResponse(int id)
{
//some logic
await Task.Run(() => { B.Update(id); });
//some logic
return View(...);
}
but it adds some overhead of utilizing a thread from the pool.
Let's say that I have a class with members which require asynchronous actions to initialize (such as file i/o or web requests). I only need to initialize once, and I don't want to reinitialize.
Are Tasks and Async-Await a good fit to accomplish this?
Here's an example of what I'm currently doing:
private Task _initializeTask;
public Task InitializeAsync()
{
return _initializeTask ?? (_initializeTask = Task.Run(
async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
}));
}
Does this do what I think it does? Are there better ways to do it?
Is it thread safe? Not a requirement but should be considered.
Edits:
What I think it does? I believe that if _initializeTask hasn't been assigned then it will be assigned a new task that will kick off and then await the async lambda contained within. Any subsequent calls to the method will await the already running (or completed) task that was assigned to _initializedTask.
When do I want it to construct? Typically I'd use this sort of method on a service that I resolve with an IoC container. Multiple dependent classes can be constructed with a reference to the class. Then, before use, each of them awaits InitializeAsync(). If theres multiple dependent classes then I don't want to double up on initializing it.
Factory Method? There usually won't be multiple instances constructed that need to be initialized, so Factory method doesn't seem like a good solution. I've used something like a "static CreateAsync()" method for things like folder wrapper classes, but that didn't let me inject initialized folders into constructors. Async Factory methods don't gain anything when they can't be used with IoC constructor injection.
Your code will work but it is not thread safe, _initializeTask can be changed after checking it for null and before initializing it. This will result in two initializations. I would consider using AsyncLazy<T>, which is inherits from Lazy<T> which is thread safe.
Then assuming LoadAsync returns Task rather than Task<T>, your code becomes (untested):
private AsyncLazy<object> initializeTask = new AsyncLazy<object>(async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
return null;
});
public Task InitializeAsync()
{
return _initializeTask.Value;
}
You can also define a non-generic version of `AsyncLazy, so you don't have to return a value from the initialization routine.
public class AsyncLazy : Lazy<Task>
{
public AsyncLazy(Func<Task> taskFactory) :
base(() => Task.Run(taskFactory)) { }
}
Then you can initialize it using an initialization method, however that method is required to be static by the compiler:
private AsyncLazy _initializeTask = new AsyncLazy(LoadStorageAsync);
private static async Task LoadStorageAsync()
{
// Do an action requiring await here
await _storageField.LoadAsync();
}
public Task InitializeAsync()
{
return _initializeTask.Value;
}
Run the asynchronous task from a regular initialization function. In the regular function, check if your app already has a loaded data set that matches your expectations. If the data is not present THEN call the async function.
...
If (BooleanFunctionToDetermineIfDataIsNotPresent){FunctionToLoadFreshData}
...
Private Async Void FunctionToLoadFreshData{...}
The function to load the data must not return a value lest it become a task itself.
If you work in a WebHost (ASP.NETCore app) or generic Host environment you can use simply way to do that with nuget HostInitActions
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IService, MyService>();
services.AddAsyncServiceInitialization()
.AddInitAction<IService>(async (service) =>
{
await service.InitAsync();
});
}
This nuget ensures that your initialization action will be performed asynchronously before the application starts.
Another advantage of this approach is that this initialization action can be defined from any place where services are installed into the IServiceCollection (For example, in an extension method in another project that installs internal implementations of public interfaces). This means that the ASP.NET Core project does not need to know what service and how it should be initialized, and it will still be done.
My MVC application consumes a library and some methods of this library call WCF service internally. All methods exposed from this DLL are sync (none of them return Task or Task) and since we don't own that assembly, it is not possible to convert them into Async API.
However, because these methods call WCF service, they are network bound (so ideally they should be async).
I want to use async controller actions in my MVC application to make it more scalable. My question is how to make the entire method pipeline await able when one method is sync in nature.
Async action --> await async method --> await async method 2 --> sync method from library?
Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?
Also, if I use above approach, will my code more scalable than sync version?
My question is how to make the entire method pipeline await able when one method is sync in nature.
You can't. The only solution is to rewrite the dll.
Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?
Neither.
Also, if I use above approach, will my code more scalable than sync version?
No. It will be slightly less scalable.
TaskCompletionSource<T> is a way to create a puppet Task, which can complete at any point you like, and can make it fault at any point you like. This means, this would ideal in your case since you have no control over the API method, which you are trying to consume. Following example will give you a head start.
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
ViewBag.Message = await ProcessRequest();
return View();
}
//TResult -> can be of any built-in or custom type that you should decide.
Task<TResult> ProcessRequest()
{
// Make a TaskCompletionSource so we can return a puppet Task
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
// Call your sync API method
SyncAPI syncApi = new SyncAPI();
// Call api method and set the result or exception based on the output from the API //method.
tcs.SetResult(TResult);
// Return the puppet Task, which isn't completed yet
return tcs.Task;
}
}
I have the following mvc action.
public async Task<JsonResult> DoSomeLongRunningOperation()
{
return await Task.Run(() =>
{
//Do a lot of long running stuff
//The underlying framework uses the HttpContext.Current.User.Identity.Name so the user is passed on the messagebus.
}
}
In the task the HttpContext gets null. We did a lot of tricking, but nothing assures us of the HttpContext being available always in our new thread.
Is there a solution to use HttpContext within out async tasks?
In our IocContainer we have registered the following object which passes the username to the framework.
public class HttpContextUserIdentityName : ICredentials
{
public string Name
{
get { return HttpContext.Current.User.Identity.Name; }
}
}
This code is called in a lot of places before persisting to the database.
We need either another way of getting the username of the user initiated the webrequest or fix the issue with the HttpContext being null.
Because the persisting to the database happens in the Task I can't access the HttpContext before entering the task.
I also can't think of a safe way to temporary persist the username so I can implement another ICredentials service object.
You almost never want to use Task.Run in an ASP.NET method.
I think the cleanest solution (but the most work) is to implement async-compatible interfaces at your other layers:
public async Task<JsonResult> DoSomeLongRunningOperation()
{
//Do a lot of long running stuff
var intermediateResult = await DoLongRunningStuff();
return await DetermineFinalResult(intermediateResult);
}
You should get whatever information you need from the current context before you start the new thread. In this case, add something like:
string username = HttpContext.Current.User.Username;
before Task.Run and then use that inside of the other thread.
On a side note, as it stands, there's no reason to await the task. You can just return the task directly and not mark the method as Async.
If you need to access the Response object, which will presumably to utilize the results of the long running operation and thus can't be before Task.Run you should do so after the Task.Run (but ensure that the task is awaited). If you end up doing this then you can't do what I suggested in my previous paragraph.
I would try passing in the reference to the HttpContext as the state object, because that should create a new instance of that object on the stack for the thread that executes the work. Instead of using Task.Run, use
return await Task.Factory.StartNew((ctx) =>
{
var context = (HttpContext)ctx;
//Do stuff
}, httpContextObject);
Task.Run and Task.Factory.StartNew return immediately, so asp.net continues on in the event lifecycle in the worker thread that is handling the request while your thread is operating on the object that has already been disposed.