Can Response.OnStarting be used with async await? - asp.net-core

I would like to to use the Response.OnStarting to execute some code which leverages the async await pattern. The Response.OnStarting method is defined as
public void OnStarting(Func<Task> callback)
And I often see it used to modify headers via code like the following:
context.Response.OnStarting( () => {
//modify the headers or do some synchronous work
});
However I need to do some async work from the OnStarting delegate. Let's say the method performing the async work is declared as follows:
public Task DoWorkAsync() {
//write to a db or do something else async
return Task.CompletedTask;
}
Is it valid to call this method from via an OnStarting delegate using the following approach?
context.Response.OnStarting(async () => {
await DoWorkAsync();
});
In Visual Studio this compiles without warnings and it appears to work. However it seems odd to me that the Response.OnStarting method could be used to make both synchronous and async calls. Sometimes the async await world still gets me scratching my head a bit. Can you throw light on whether this approach works and why? And why it is that Response.OnStarting can be used with a sync and async anonymous method (if in fact it can)?
Update
I did some digging to see if I could find the code that gets calls the delegate passed to the OnStarting method.
What I found was that the DefaultHttpRespose object contains the OnStarting method and inside that method it basically calls the IHttpResponseFeature.OnStarting, see here:
public override void OnStarting(Func<object, Task> callback, object state)
{
if (callback == null)
{
throw new ArgumentNullException(nameof(callback));
}
HttpResponseFeature.OnStarting(callback, state);
}
link to source code
But interestingly, the only place I can find an implementation of IHttpResponseFeature.OnStarting is here with this code:
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
{
var register = Prop<Action<Action<object>, object>>(OwinConstants.CommonKeys.OnSendingHeaders);
if (register == null)
{
throw new NotSupportedException(OwinConstants.CommonKeys.OnSendingHeaders);
}
// Need to block on the callback since we can't change the OWIN signature to be async
register(s => callback(s).GetAwaiter().GetResult(), state);
}
But that implementation blocks on the async delegate call. There is even a comment to that effect but I don't understand why that was necessary? And I'm not sure this is the implementation of IHttpResponseFeature.OnStarting that is getting ran, it's just the only one I could find on github. Any help greatly appreciated.

However it seems odd to me that the Response.OnStarting method could be used to make both synchronous and async calls.
Synchronous APIs must be implemented synchronously. A Func<int> must return an int; it cannot return a Task<int> instead.
Asynchronous APIs may be implemented asynchronously or synchronously. A Func<Task<int>> may have an asynchronous implementation, returning a Task<int> that will complete in the future. Or it may have a synchronous implementation, returning a Task<int> that is already completed with an int value.
When dealing with asynchronous APIs (including interfaces and delegate signatures), synchronous implementations are entirely permissible.
I wouldn't be distracted by the blocking implementation. The comment indicates that OWIN has a synchronous API in a place where there should be an asynchronous API, and IHttpResponseFeature.OnStarting is just hiding that oddity from you.

Related

How to guard against double trigger of async method in Blazor wasm

I'm developing a blazor wasm application. I'm struggling with an async issue - that I find difficult to tackle, as the threading in the browser is not completely clear to me. I'm not using any async voids - everything is async Task.
From an API call (async) I get back two objects. One object I dispatch as an synchronous update to the fluxor store, and the other I have to do a async call on the local Indexdb, after which this one also enters the fluxor store.
The two fluxor store updates trigger via an event an update method of the view model. This update method is async, as it also get's some info from the IndexedDb. This async method fetches async some items from Indexdb, clears a dictionary, then enumerates over a property of the fluxor store to update the model.
This method get's called twice in quick succession, and as a result, starts interweaving.
The trouble is the first method call is paused during the enumeration over the state, next the second method call clears the dictionary, and starts it's own enumeration (and finishes), after which the first method resumes midst it's earlier started enumeration.
This results in errors - trying to add identical keys twice to the dictionary.
How can I guard against this?
How can I prevent the same method call to interweave with itself in Blazor wasm?
How can the (synchronous) enumeration part of the async update method be paused, allowing the full method to run, and next resuming the first call again?
public partial class DebugPage : BasePage, IDisposable
{
[Inject] IState<MonthBalanceState> MonthBalanceState { get; set; }
private Dictionary<IMonthBalanceAddress, int> DbCountDictionary = new Dictionary<IMonthBalanceAddress, int>();
protected override async Task OnParametersSetAsync()
{
MonthBalanceState.StateChanged += async (_, _) => await MonthBalanceState_StateChanged();
Console.WriteLine($"Linkage made to Monthbalance State");
await base.OnParametersSetAsync();
}
//This method get's called twice quickly - starting the interweaving
private async Task MonthBalanceState_StateChanged()
{
Console.WriteLine($"Update via Statechanged Monthbalance State");
var result = await UpdateDictionaryAsync();
}
private async Task<bool> UpdateDictionaryAsync()
{
DbCountDictionary.Clear();
Log.Debug("Debug dictionary updated");
foreach (IMonthBalanceLoadable mb in MonthBalanceState.Value.MonthBalances.ToList())
{
Console.WriteLine($"Adding {mb.Address.ToString()}");
DbCountDictionary.Add(mb.Address, await Db.GetCountByAddress(mb.Address));
}
return true;
}
+= async (_, _) => await MonthBalanceState_StateChanged();
This lambda is an async void wrapping your handler.
So the whole is not awaited and that is the source of your problem.
That StateChanged event should probably be an EventCallback property. Post the relevant code if you need more help.
If possible, you could use OnAfterRenderAsync(bool firstRender) instead if OnParametersSetAsync() and then only set you variables if it is the first render.

How to test with Response.OnCompleted delegate in a finally block

I have the following netcore 2.2 controller method that I am trying to write an xUnit integration test for:
private readonly ISoapSvc _soapSvc;
private readonly IRepositorySvc _repositorySvc;
public SnowConnectorController(ISoapSvc soapSvc, IRepositorySvc repositorySvc)
{
_soapSvc = soapSvc;
_repositorySvc = repositorySvc;
}
[Route("accept")]
[HttpPost]
[Produces("text/xml")]
public async Task<IActionResult> Accept([FromBody] XDocument soapRequest)
{
try
{
var response = new CreateRes
{
Body = new Body
{
Response = new Response
{
Status = "Accepted"
}
}
};
return Ok(response);
}
finally
{
// After the first API call completes
Response.OnCompleted(async () =>
{
// Run the close method
await Close(soapRequest);
});
}
}
The catch block runs and does the things it needs to, then the finally block runs and does things it needs to do after the request in the catch finishes per design.
Close has been both a private method . It started as a public controller method but I don't need to expose it for function so moved it to private method status.
Here's an integration test I started with the intention of just testing the try portion of the code:
[Fact]
public async Task AlwaysReturnAcceptedResponse()
{
// Arrange------
// Build mocks so that we can inject them in our system under tests constructor
var mockSoapSvc = new Mock<ISoapSvc>();
var mockRepositorySvc = new Mock<IRepositorySvc>();
// Build system under test(sut)
var sut = new SnowConnectorController(mockSoapSvc.Object, mockRepositorySvc.Object);
var mockRequest = XDocument.Load("..\\..\\..\\mockRequest.xml");
// Act------
// Form and send test request to test system
var actualResult = await sut.Accept(mockRequest);
var actualValue = actualResult.GetType().GetProperty("Value").GetValue(actualResult);
// Assert------
// The returned object from the method call should be of type CreateRes
Assert.IsType<CreateRes>(actualValue);
}
I am super new to testing... I've been writing the test and feeling my way through the problem. I started by entering the controller method not really knowing where it would go. The test works through the try method, and then an exception is thrown once it hits the delegate in the finally block.
It looks like my test will have to run through to the results of the finally block unless there is a way to tell it to stop with the catch blocks execution?
That's fine, i'm learning, but the problem with that approach for me now is that the HttpResponse's Response.OnCompleted delegate in the finally block returns null when my test is running and I haven't been successful at figuring out what I can do to not make it null - because it is null, it throws this when my unit test is executing -
System.NullReferenceException: 'Object reference not set to an instance of an object.'
*One thought that occurred was that if I was to make the private Close method a public controller method, and then make the Accept method not have the finally block, I could create a third controller method that does the try finally action by running the two controller methods and then just test the individual controller methods that are strung together with the third. However, it doesn't feel right because I would be exposing methods just for the sake of unit testing and I don't need Close to be exposed.
If the above idea is not the right approach, I am wondering what is, and if I just need to test through end to end, how I would get over the null httpresponse?
Any ideas would be appreciated. Thank you, SO community!
EDIT - Updated Test that works after the accepted answer was implemented. Thanks!
[Fact]
public async Task AlwaysReturnAcceptedResponse()
{
// Arrange------
// Build mocks so that we can inject them in our system under tests constructor
var mockSoapSvc = new Mock<ISoapSvc>();
var mockRepositorySvc = new Mock<IRepositorySvc>();
// Build system under test(sut)
var sut = new SnowConnectorController(mockSoapSvc.Object, mockRepositorySvc.Object)
{
// Supply mocked ControllerContext and HttpContext so that finally block doesnt fail test
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext()
}
};
var mockRequest = XDocument.Load("..\\..\\..\\mockRequest.xml");
// Act------
// Form and send test request to test system
var actualResult = await sut.Accept(mockRequest);
var actualValue = actualResult.GetType().GetProperty("Value").GetValue(actualResult);
// Assert------
// The returned object from the method call should be of type CreateRes
Assert.IsType<CreateRes>(actualValue);
}
Curious what you are doing in the Close method against the input parameter.
Does it have to happen after response is being sent? It might not always happen as you would expect, see here.
Regardless though, during runtime asp.net core runtime sets a lot of properties on the controller including ControllerContext, HttpContext, Request, Response etc.
But those won't be available in unit testing since there is no asp.net core runtime there.
If you really want to test this, you'll have to mock them.
Here is the ControllerBase source code.
As we can see, ControllerBase.Response simply returns ControllerBase.HttpContext.Response, and ControllerBase.HttpContext is a getter from ControllerBase.ControllerContext. This means you'll have to mock a ControllerContext (and the nested HttpContext as well as HttpResponse) and assign it to your controller in the setup phase.
Furthermore, the OnCompleted callback won't get called in unit test either. If you want to unit test that part, you'll have to trigger it manually.
Personally I think it's too much hassle beside the open bug I mentioned above.
I would suggest you move the closing logic (if it's really necessary) to a IDisposable scoped service and handle that in the Dispose instead - assuming it's not a computation heavy operation which can impact the response latency.

FindAsync never comes back however Find works just fine

I am using FluentValidation to validate the objects. I am simply checking checking whether the user exists in database or not. In my case, DbContext.Entity.Find works just fine but DbContext.Entity.FindAsync never returns.
Please refer to the below source code where it is happening.
public class ChangeStatusOfUserCommandValidator : AbstractValidator<ChangeStatusOfUserCommand>
{
private readonly FieldSellDbContext dbContext;
private ChangeStatusOfUserCommandValidator()
{ }
public ChangeStatusOfUserCommandValidator(FieldSellDbContext databaseContext)
{
dbContext = databaseContext;
RuleFor(u => u.UserId).NotEmpty();
RuleFor(u => u.UserId).MustAsync(UserExists).WithMessage("Provided user id already exists in the database.");
}
public async Task<bool> UserExists(int value, CancellationToken cancellationToken)
{
var user = await dbContext.Users.FindAsync(value, cancellationToken);
//var user = dbContext.Users.Find(value); --Works fine even in async method
return user != null;
}
}
Thanks
Your problem is almost certainly further up your call stack, where the code is calling Task<T>.Result, Task.Wait(), Task.GetAwaiter().GetResult(), or some similar blocking method. If your code blocks on asynchronous code in a single-threaded context (e.g., on a UI thread), it can deadlock.
The proper solution is to use async all the way; that is, use await instead of blocking on asynchronous code. Fluent validation has an asynchronous workflow (e.g., ValidateAsync, MustAsync), so you'll need to be sure to use that rather than the synchronous APIs.

async method does not continue when await statement returns

I'm using MVC 4 and I have the following code :
public void DoWork(string connectionId)
{
connectionId = this.connectionId;
var a = MakeADelayAsync();
}
public async Task MakeADelayAsync()
{
await Task.Delay(5000);
var generalHubContext = GlobalHost.ConnectionManager.GetHubContext<GeneralHub>();
generalHubContext.Clients.Client(connectionId).showNotification("Completed");
}
"DoWork" method is my mvc action. what I intent to do is when the action button is pressed the "DoWork" calls an async method and returns to the client immediately. when the async method has completed his job it will notify client using signalR.
The problem is in the "MakeADelayAsync" method, those two lines after await won't be called ever. It seems that the flow never continues after await.
First question is Where is the problem in "MakeADelayAsync" ?
Second question is why do I have to write a useless code of var a = MakeADelayAsync(); to avoid compiler warning while I'm completely aware of what I am doing? I never use "a" anyway.
"DoWork" method is my mvc action. what I intent to do is when the action button is pressed the "DoWork" calls an async method and returns to the client immediately. when the async method has completed his job it will notify client using signalR.
Doing this is extremely dangerous. I strongly recommend that you use a persistent queue, as I said in my previous answer: Azure queue, MSMQ, WebSphere MQ, etc.
However, if you insist on doing it the dangerous way, then you can use the code that I have on my blog to execute background work on ASP.NET outside of a request context:
public void DoWork(string connectionId)
{
connectionId = this.connectionId;
// This is extremely dangerous code! If ASP.NET unloads
// your app, then MakeADelayAsync may not run to completion.
BackgroundTaskManager.Run(() => MakeADelayAsync());
}
First question is Where is the problem in "MakeADelayAsync" ?
You're executing code that is attempting to resume on the request context after the request is completed and the request context is disposed. It's the same problem you had before.
Second question is why do I have to write a useless code of var a = MakeADelayAsync(); to avoid compiler warning while I'm completely aware of what I am doing?
The compiler warning is telling you that the code is almost certainly a mistake... and the compiler is right.
can you try to mark your DoWork method as async?
public async void DoWork(string connectionId)
{
connectionId = this.connectionId;
var a = MakeADelayAsync();
}

Continuations using Async CTP

Is it possible to use Async CTP to emulate continuations and tail recursion?
I'm thinking something along the lines of:
async Task Loop(int count)
{
if (count == 0)
retrun;
await ClearCallStack();
//is it possible to continue here with a clean call stack?
Loop(count -1)
}
I guess one needs a custom scheduler and such, but would it be possible?
(that is, can it be used to recurse w/o blowing the call stack)
Yes, this is entirely possible.
In the newest Async CTP (Refresh for VS2010 SP1), there's a "GeneralThreadAffineContext" class in the Unit Testing sample (either in VB or C#). That provides the requisite helper code for just running an async method in a general purpose thread-affine manner.
By thread affinity, we mean that the async continuations get processed on the same context as the original thread, similarly to the behavior for WinForms/WPF, but without spinning up the real WPF or WinForms message loop.
Task.Yield()'s design is to defer the rest of the current method to the SynchronizationContext, so you don't even need to write your own await ClearCallStack(). Instead, your sample will boil down to:
async Task DoLoop(int count)
{
// yield first if you want to ensure your entire body is in a continuation
// Final method will be off of Task, but you need to use TaskEx for the CTP
await TaskEx.Yield();
if (count == 0)
return;
//is it possible to continue here with a clean call stack?
DoLoop(count -1)
}
void Loop(int count)
{
// This is a stub helper to make DoLoop appear synchronous. Even though
// DoLoop is expressed recursively, no two frames of DoLoop will execute
// their bodies simultaneously
GeneralThreadAffineContext.Run(async () => { return DoLoop(count); });
}