I want to configure a HttpClient to have a default authorization header that results from an async call like this:
builder.Services.AddHttpClient("client", (serviceProvider, client) =>
{
client.DefaultRequestHeaders.Authorization =
serviceProvider.GetService<IAuthProvider>()?.GetAuthHeaderAsync().Result;
});
Is it ok to call Task.Result in configuration methods that do not support async delegates? or should I do this:
builder.Services.AddHttpClient("client", async (serviceProvider, client) =>
{
client.DefaultRequestHeaders.Authorization =
await serviceProvider.GetService<IAuthProvider>()?.GetAuthHeaderAsync();
});
When I do this second option I'm getting a warning: Avoid using
'async' lambda when delegate type returns 'void'
Dependency injection is one place where the "use async all the way" general advice isn't applicable, since no DI/IoC container supports asynchronous resolution.
One way to work around this is to use "asynchronous factory" types; i.e., you define an HttpClient factory that can asynchronously produce a client on demand.
Another approach is to just do DI/IoC injection synchronously, usually blocking on all asynchronous construction at startup time, before the application has actually started.
A final approach is more advanced: you define a type that wraps all the APIs you need (with asynchronous signatures), and you hide the asynchronous initialization behind an async lazy (or a single-item async cache) within that type.
Regarding your specific use case, is the authentication header something that is only requested once, and remains constant for the rest of the app? If so, I would say to just synchronously block on it at startup. If it's something that can change, then you're probably better off handling authentication as a message handler, not at the point of constructing a client.
Related
When making HTTP calls using an instance of HttpClient wrapped in Polly's retry policy and injected into a controller using dependency injection, I want to send ILogger<T> from the call site to a delegate of the retry policy (e.g., onRetry), so logs are registered more appropriately.
Polly docs explain how to achieve this by sending ILogger<T> from the calls site to the retry delegates leveraging Context encapsulated in an HttpRequestMessage request.
However, this solution works when you leverage a method of the HttpClient that takes HttpRequestMessage in one of its overloads. For instance, client.SendAsync.
However, not every method of HttpClient take HttpRequestMessage. For instance, I'm using client.GetStreamAsync, which none of its overloads take HttpRequestMessage.
In this case, I wonder how you would pass the Ilogger<T> to Polly's retry delegates.
Options that does not work for your use case
Using the Context object with HttpRequestMessage
As you have stated in your question this is not applicable, since you don't have a HttpRequestMessage instance on which you could access the Context via the request.GetPolicyExecutionContext call.
Using AddPolicyHandler + IServiceProvider
The AddPolicyHandler has an overload which provides access to the IServiceProvider and to the HttpRequestMessage. You could obtain an ILoggerFactory via provider.GetRequiredService<ILoggerFactory>() and then you could call factory.CreateLogger<T>.
The problem with this approach is that you don't know T at policy registration time, since you want to use the Controller as T.
Options that could work for your use case
Defining the policy inside your Controller
If you would define the policy inside the same class where you have the intention to use it then you could access the ILogger<YourController>.
There are two drawbacks of this approach:
You have to define (more or less) the same policy in every place where you want to use it
You have to explicitly call the ExecuteAsync
The first issue can be addressed via the PolicyRegistry
Registering the policy into PolicyRegistry and using Context
You can register your policy/ies into a PolicyRegistry and then you can obtain them (via IReadOnlyPolicyRegistry) inside your controller. This approach lets you define your policy in the way that you can retrieve an ILogger from the Context inside the onRetry. And you can specify the Context when you call the ExecuteAsync
var context = new Polly.Context().WithLogger(yourControllerLogger);
await policy.ExecuteAsync(async (ct) => ..., context);
Registering the policy into PolicyRegistry and using try-catch
The previous approach used the Context to transfer an object between the policy definition and its usage. One can say that this separation is a bit fragile since the coupling between these two is not explicit rather via a magic Context object.
An alternative solution could be to perform logging only inside your the ExecuteAsync to avoid the usage of the Context
await policy.ExecuteAsync(async () =>
try
{
...
}
catch(Exception ex) //filter for the retry's trigger exception(s)
{
yourControllerLogger.LogError(...);
});
As you can see none of the above solutions is perfect since you want to couple the policy and its usage via logging.
UPDATE #1
I'm not a big fan of defining policy inside a controller, because I generally reuse a policy (and accordingly the HttpClientFactory) in different controllers.
As I said above, this is one option out of three. The other two options do not require you to define your policy inside the controller class. You can define them inside the startup
var registry = new PolicyRegistry()
{
{ "YourPolicyName", resilientStrategy }
};
services.AddPolicyRegistry(registry);
and then retrieve the given policy inside the controller
private readonly IAsyncPolicy policy;
public YourController(IReadOnlyPolicyRegistry<string> registry)
{
policy = registry.Get<IAsyncPolicy>("YourPolicyName"):
}
I suppose there is no other cleaner solution
If you want to / need to use the controller's logger inside the onRetry delegate then I'm unaware of any cleaner solution.
If you want to use that logger to be able to correlate the controller's log with the policy's log then I would rather suggest to use a correlation id per request and include that into your logs. Steve Gordon has a nuget package called correlationId which can help you to achieve that.
Here's the workflow:
Incoming HTTP request to WebApi2 endpoint.
Make synchronous (e.g. not async) call to get some data.
Map response from DB entity to API model.
a. Executes AutoMapper mapping.
b. Includes the following snippet (see below).
c. If operation is "quick", no issue. If operation is "slow", then "a task was cancelled" exception is thrown.
I get lucky in cases when the mapping action is quick. But if I add a Task.Delay(2000), then I get the exception in question. It seems that ASP.NET is not "waiting" for my async lamba to complete?
Here is the body of the mapping expression:
mapping.AfterMap(async (entity, model) => {
var child = await _childRepo.Get(entity.ChildId);
await Task.Delay(2000); // For testing, of course.
if (child != null)
{
// Fill in some properties on model
}
});
Note that this is example code, and I don't intend to make additional DB/repo calls during mapping in "real life".
AfterMap takes an Action, which is a synchronous delegate, not an asynchronous delegate (as I explain on my blog). As such, it does not work as expected with async lambdas.
In this case (since the delegate returns void), the compiler will actually allow an async lambda; however, it will compile to an async void method. (The compiler does this to allow async event handlers). As I describe in my MSDN article on async best practices, you should avoid async void.
One of the reasons to avoid async void is that it is very difficult to detect when an async void method has completed. In fact, (with the exception of WebForm lifetime events), ASP.NET will not even attempt to do so.
I've build an API-endpoint to fetch available languages from. In my MVC application, I have a helper to fetch the languages async. The method is defined like:
public static async Task<Languages> GetLanguagesAsync()
{
var apiResponse = await APIHelper.GetContentGetAsync("Languages").ConfigureAwait(false);
return apiResponse.LanguagesDataModel;
}
In my View I want to bind a dropdownlist to the list of available languages the user can select from.
#Html.DropDownListFor(m => m.Language, LanguageHelper.AvailableLanguages)
The getter is defined the following:
public static IEnumerable<SelectListItem<string>> AvailableLanguages
{
get
{
var result = GetLanguagesAsync().Result;
return new List<SelectListItem<string>>(result.Languages.Select(l => new SelectListItem<string> {Value = l.Key, Text = l.Value}));
}
}
However, I always get an error at line var result = GetLanguagesAsync().Result; which is the most upvoted answer from here.
The exception thrown is
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.
As stated here the called action is marked async.
Razor code today cannot handle asynchronous calls, even if you're wrapping those calls with synchronous blocking (which is a design mistake). The fact that the action is async is immaterial, because this is during the processing of the view, after the action has completed.
Async views have been added to ASP.NET Core; however, it should still be an uncommon use case.
In general, your options are:
Make the helper synchronous "all the way"; i.e., use synchronous API calls instead of asynchronous, and get rid of the sync-over-async code.
Add the data to your view models (possibly as part of a common VM base).
In this specific case, I agree with Panagiotis' comment that this kind of unchanging information should only be loaded once per app, not once per call. Wrapping a synchronous implementation inside a Lazy<T> would be the easiest solution.
I am writing a WCF webservice that includes a method that accepts an array of objects and inserts them into the database. This could take a long time, so I can't just expect the client to wait.
My colleague thinks that I don't need to do anything, that it's the client's job to call my service asynchronously. I just write a normal method. This doesn't sound right to me, although I hope it's true because looking at WCF async tutorials and SO questions has just confused me so far.
Is he correct? If not, how do I actually write the method in a way that would allow the client to call the method asynchronously or otherwise avoid hanging?
If he is correct (as appears to be the case), then what is the point of defining an asynchronous method ([OperationContract (AsyncPattern=true)], Begin, End, etc.). Is it a way explicitly handling asynchronous calls, or allowing interactivity, or what?
It should fall on the client's side. They are the ones that have to prevent their app/UI from hanging.
Have your client call your method asynchronously. If they are using a service reference, all methods/events are generated automatically.
myWcfClient.myMethodCompleted
+= new EventHandler<myMethodCompletedEventArgs>(myCallBack);
myWcfClient.myMethodAsync(args);
public void myCallback(object sender, myMethodCompletedEventArgs e)
{
var myResult = e.Result;
}
If your client doesn't care what happens with the service call, you want a simple fire and forget operation and you can do this.
The AsyncPattern property tells the runtime that your operations implement the .NET Framework asynchronous method design pattern. See here. If you want your client application to know what has happened with your service call then you can use this pattern. There are other ways to get the results though.
This is only on the client side, I've skipped the old event driven async bleh pattern and replaced it with the async-await pattern. Not waiting for webmethod calls async, and blocking the UI... doesn't even belong in this century ;)
If you are using .net 4.5+ you are getting the async-await pattern for free (Unless wp8, where you still have to wrap it). The async methods should already be avaliable through the service. I recommend the AsyncBridge if you are using old frameworks, which allows you to use the async-await pattern for cases like this. The alternative is to stick to the old event driven async nightmare. The examples below is only possible if you are using C#5.0 or never.
Ensure to start in a new thread from a non async method.
Task.Factory.StartNew(client.DoSomethingAsync("blabla") ).ContinueWith(...);
The last part is run after your method has completed, check for exceptions to completion code etc.
Or in some async method
public async Task<string> DoSomethingAsync(String text) {
// Exception handling etc
return await client.DoSomethingAsync(text);
}
wrapping APM to async-await pattern:
public class ServiceWrapper : IServiceWrapper
{
readonly YourServiceClient client;
public ServiceWrapper(YourServiceClient client)
{
this.client = client;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
return await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
}
}
EDIT
Opening and closing connections in a wrapped service. (I don't have my devbox avaliable right now but this should work).
public class ServiceWrapper : IServiceWrapper
{
EndpointAddress address;
public ServiceWrapper(EndpointAddress clientAddress)
{
address = clientAddress;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
// handle exceptions etc here, can be done some cleaner..
var client = new YourServiceClient();
client.Endpoint.Address = address.Address; // can skip this..
await client.OpenAsync()
var res = await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
await client.CloseAsync();
return res;
}
}
One last thing I'm not sure how you generate your proxy, if you are using vs make sure to hook of the checkbox allowing async methods when configuring the service. If you are using svcutil add the appropriate flags.
Async-await pattern
Old event driven async pattern
Hope it helps,
Cheers,
Stian
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;
}
}