Async await method gets stuck - c#

I have an API method with this signature:
public Task<MResponse> StartAsync(MRequest request)
I can't change that (I can change the implementation inside but not the signature of the method).
This is how I call this method:
var ret = await _SyncClient.StartAsync(Request);
This is the implementation
public Task<MResponse> StartAsync(MRequest request)
{
if (request.Number == 1)
{
return new Task<MResponse>(() => new MResponse() { Description = "Error", Code = 1 });
}
else
{
return new Task<MResponse>(() => new MResponse() { Description = "", Code = 0 });
}
}
On the debug mode I see that my code get into StartAsync and and the return and finish it, but its look like the wait never ends....
I have no idea what it can be.
If you need more information about my code, let me know.

It looks like you're not actually having anything asynchronous to do in the implementation. That's fine, there's always cases where the signature is set (i.e. an interface) and some implementations don't need it.
You can still make the method async and just return the response as if it were as synchronous method, like so:
public async Task<MResponse> StartAsync(MRequest request)
{
return new MResponse() { ... }
}
But that will probably trigger Intellisense to tell you there's nothing being awaited in your method. A better approach is to use Task.FromResult like so:
public Task<MResponse> StartAsync(MRequest request)
{
return Task.FromResult(new MResponse() ... );
}
which will return a Task that already as completed and therefore will not block your await in the follow-up.

You are creating a task by calling the Task Constructor
This creates a Task, but does not start it. You would have to explicitly call Task.Start
public Task<MResponse> StartAsync(MRequest request)
{
var task = (request.Number == 1)
? new Task<MResponse>(() => new MResponse() { Description = "Error", Code = 1 })
: new Task<MResponse>(() => new MResponse() { Description = "", Code = 0 });
task.Start();
}
You have other options here. You can create the object on the current thread and return it as a completed task
return Task.FromResult<MResponse>(new MResponse(...))
Another option is to invoke Task.Run to kick off the work on a background thread.
public Task<MResponse> StartAsync(MRequest request)
{
Func<MResponse> generateResponse = (request.Number == 1)
? () => new MResponse() { Description = "Error", Code = 1 })
: () => new MResponse() { Description = "", Code = 0 });
return Task.Run(generateResponse);
}

Related

Set maximum runtime of a method

If I have a method where I want to perform some (potentially) long-running function and I want to put a limit on its execution time, I've been using this pattern (please pardon any errors in the code, typed by hand, not in an IDE, this is a simplification of a larger piece of code).
public string GetHello()
{
var task = Task.Run(() =>
{
// do something long running
return "Hello";
});
bool success = task.Wait(TimeSpan.FromMilliseconds(1000));
if (success)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}
If I want to use the GetHello method in an async capacity, i.e. public async Task<string> GetHello(), how would I do this while hopefully preserving a similar pattern? I have the following, but I get compiler warnings about This async method lacks 'await' operators and will run synchronously as expected.
public async Task<string> GetHello()
{
var task = Task.Run(async () =>
{
// await something long running
return "Hello";
});
bool success = task.Wait(TimeSpan.FromMilliseconds(1000));
if (success)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}
I just don't know how to change this or where I would put await in order for this to work as expected.
You can combine using CancellationToken and awaitable Task.WhenAny to achieve desired behavior:
public async Task<string> GetHello()
{
var cts = new CancellationTokenSource();
var task = Task.Run(async () =>
{
// await something long running and pass/use cts.Token here too
return "Hello";
}, cts.Token);
var delay = Task.Delay(1000, cts.Token);
var finishedFirst = await Task.WhenAny(task, delay);
cts.Cancel();
if (finishedFirst == task)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}

Query parameters are persisting values

I have to simulate a real-time data flow from the server, for that I have implemented a Timer class from the System.Threading namespace.
public class DataManager
{
private Timer _timer;
private AutoResetEvent _autoResetEvent;
private Action _action;
public DateTime TimerStarted { get; }
public DataManager(Action action)
{
_action = action;
_autoResetEvent = new AutoResetEvent(false);
_timer = new Timer(Execute, _autoResetEvent, 0, 10000);
TimerStarted = DateTime.Now;
}
public void Execute(object stateInfo)
{
_action();
if ((DateTime.Now - TimerStarted).Seconds > 60)
{
_timer.Dispose();
}
}
}
And using this class to execute repository method to get latest data from controller.
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PagedTransactionDataRequest queryParams)
{
var pageSize = queryParams.PageSize ?? 1;
var pageNumber = queryParams.PageNumber ?? 10;
<b>var timerManager = new DataManager(async () =>
await _paymentDraftHub.Clients.All.SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetAllDeclinedAsync(pageSize, pageNumber))
);</b>
var response = new ResponseMessage { Message = "Accepted", Code = "201" };
return Ok(response);
}
PROBLEM: First request to the API is working perfectly fine but during the second request pageSize and pageNumber have the old values too. When the timer runs every 10 seconds, I can see the old query parameter values and new query parameter values both are getting executed.
I am not sure why the old values are even getting executed.
When you call your first GET, you create a DataManager. That DataManager only knows about the first query params. It then executes on a timer with those parameters.
When you call your second GET, you are creating a second DataManager. That second manager will also start executing on a timer with the 2nd set of parameters, but it does not affect the first one at all.
One solution is to make the DataManager a member of your controller, and expose the desired action as a property:
private DataManager _manager;
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PagedTransactionDataRequest queryParams)
{
var pageSize = queryParams.PageSize ?? 1;
var pageNumber = queryParams.PageNumber ?? 10;
if(_manager == null) {_manager = new DataManager(async () =>
await _paymentDraftHub.Clients.All.SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetAllDeclinedAsync(pageSize, pageNumber))
);
}
else
{_manager.action = async () => await _paymentDraftHub.Clients.All.SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetAllDeclinedAsync(pageSize, pageNumber));
}
var response = new ResponseMessage { Message = "Accepted", Code = "201" };
return Ok(response);
}
I didn't have time to test this solution, so it may not even compile, but it does give you an idea.

Getting result from Func<Task<T>>

Method Under Test
protected override async Task<Name> DoExecuteAsync(NameContext context)
{
context.ThrowIfNull("context");
var request = new Request
{
Id = context.Id,
Principal = context.UserPrincipal,
};
return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
}
protected override Name HandleError(NameContext viewContext, Exception exception)
{
if (this.errorSignaller != null)
{
this.errorSignaller.SignalFromCurrentContext(exception);
}
return Name.Unknown;
}
This is implementation of
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
public async Task<TOutput> ExecuteAsync(TInput context)
{
try
{
return await this.DoExecuteAsync(context);
}
catch (Exception e)
{
return this.HandleError(context, e);
}
}
protected abstract Task<TOutput> DoExecuteAsync(TInput context);
protected virtual TOutput HandleError(TInput viewContext, Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
Test Case goes like below
[SetUp]
public void Setup()
{
var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
this.controller = new Mock<Controller>();
this.controller.Object.SetMockControllerContext(httpContext.Object);
this.repoMock = new Mock<IRepository>();
this.errorSignaller = new Mock<IErrorSignaller>();
this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
this.userPrinciple = new Mock<IPrincipal>();
this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}
[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
// Arrange
this.repoMock.Setup(
x => x.NameAsync(
It.IsAny<Request>(),
It.IsAny<CancellationToken>(),
It.IsAny<ILoggingContext>())).Throws<Exception>();
// Act
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
// Assert
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}
To verify the Name Object ,When I use the var result = await act() before the line
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
The this.errorSignaller.Verify fails since it's count is 2 instead of 1. My intention is to check the Name object along with below code.
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
I knew that if I write a new test case I can easily verify it, but is there any way I can do altogether in this test?
If you want to test the result then use:
Name result = await this.query.ExecuteAsync(this.context);
result.Should().Be(expectefResult);
Make sure to make your test method public async Task
Update
To be able to verify name you would need to set it in the function.
//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;
// Act
Func<Task> act = async () => {
actualName = await this.query.ExecuteAsync(this.context);
};
// Assert
act.ShouldNotThrow();
actualName
.Should().NotBeNull()
.And.Be(expectedName);
//...rest of code
Original
As already mentioned in the comments, act is a function that returns a Task.
While its implementation is awaited, the function itself still needs to be invoked. And since the function returns a Task it too would need to be awaited.
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();
It is the same as having the following function.
async Task<Name> act() {
return await this.query.ExecuteAsync(this.context);
}
You would have to await it the same way
var name = await act();
The only difference being that the former example has the function in a delegate.
Try to avoid mixing blocking calls like .Result with async/await code. This tends to cause deadlocks.
You can try to check it with
await query.ExecuteAsync(this.context);
or
this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();
and in case of Func:
act.Invoke().GetAwaiter().GetResult();

Method timeout without creating new thread

How I can implement timeout for a block of code in asp.net application without using Task or Thread? I don't want create new threads because HttpContext will be NULL in another threads.
For example, following code will not work
var task = Task.Run(() =>
{
var test = MethodWithContext();
});
if (!task.Wait(TimeSpan.FromSeconds(5)))
throw new Exception("Timeout");
object MethodWithContext()
{
return HttpContext.Current.Items["Test"]; // <--- HttpContext is NULL
}
EDIT:
I don't want pass current context to method, because I will have a lot of nested methods inside method... so a lot of refactor must be done for this solution
EDIT2:
I have realized that I can assign current context to variable before creating new task and replace HttpContext in task with this variable. This will be safe?
var ctx = HttpContext.Current;
var task = Task.Run(() =>
{
HttpContext.Current = ctx;
var test = MethodWithContext();
});
if (!task.Wait(TimeSpan.FromSeconds(5)))
throw new Exception("Timeout");
object MethodWithContext()
{
return HttpContext.Current.Items["Test"]; // now works correctly
}
You will need to pass the context of you main thread like this:
var task = Task.Run(() =>
{
// Takes the context of you current thread an passes it to the other thread.
var test = MethodWithContext(HttpContext.Current);
});
if (!task.Wait(TimeSpan.FromSeconds(5)))
throw new Exception("Timeout");
void object MethodWithContext(HttpContext ctx)
{
// Now we are operating on the context of you main thread.
return ctx.Items["Test"];
}
But the question is still:
Why do you want to create a task for this?
After starting the task you are simply waiting for its completion. You could just call your method synchronously. Although I am not sure how to limit the execution to 5 seconds if you do that.
As you have mentioned in the comments you'd like to get rid of the additional parameter because you have more than one method. This is how I'd do it:
public void YourOriginalMethod()
{
YourUtilityClass util = new YourUtilityClass(HttpContext.Current);
var task = Task.Run(() =>
{
var test = util.MethodWithContext();
});
if (!task.Wait(TimeSpan.FromSeconds(5)))
throw new Exception("Timeout");
}
public class YourUtilityClass
{
private readonly HttpContext _ctx;
public YourUtilityClass(HttpContext ctx)
{
if(ctx == null)
throw new ArgumentNullException(nameof(ctx));
_ctx = ctx;
}
public void object MethodWithContext()
{
return _ctx.Items["Test"];
}
// You can add more methods here...
}

Chain with LUIS Intents

So they have this nice example inside the EchoBot sample to demonstrate chains
public static readonly IDialog<string> dialog = Chain.PostToChain()
.Select(msg => msg.Text)
.Switch(
new Case<string, IDialog<string>>(text =>
{
var regex = new Regex("^reset");
return regex.Match(text).Success;
}, (context, txt) =>
{
return Chain.From(() => new PromptDialog.PromptConfirm("Are you sure you want to reset the count?",
"Didn't get that!", 3)).ContinueWith<bool, string>(async (ctx, res) =>
{
string reply;
if (await res)
{
ctx.UserData.SetValue("count", 0);
reply = "Reset count.";
}
else
{
reply = "Did not reset count.";
}
return Chain.Return(reply);
});
}),
new RegexCase<IDialog<string>>(new Regex("^help", RegexOptions.IgnoreCase), (context, txt) =>
{
return Chain.Return("I am a simple echo dialog with a counter! Reset my counter by typing \"reset\"!");
}),
new DefaultCase<string, IDialog<string>>((context, txt) =>
{
int count;
context.UserData.TryGetValue("count", out count);
context.UserData.SetValue("count", ++count);
string reply = string.Format("{0}: You said {1}", count, txt);
return Chain.Return(reply);
}))
.Unwrap()
.PostToUser();
}
However instead of using a REGEX to determine my conversation path I would much rather use a LUIS Intent. I'm using this nice piece of code to extract the LUIS Intent.
public static async Task<LUISQuery> ParseUserInput(string strInput)
{
string strRet = string.Empty;
string strEscaped = Uri.EscapeDataString(strInput);
using (var client = new HttpClient())
{
string uri = Constants.Keys.LUISQueryUrl + strEscaped;
HttpResponseMessage msg = await client.GetAsync(uri);
if (msg.IsSuccessStatusCode)
{
var jsonResponse = await msg.Content.ReadAsStringAsync();
var _Data = JsonConvert.DeserializeObject<LUISQuery>(jsonResponse);
return _Data;
}
}
return null;
}
Now unfortunately because this is async it doesn't play well with the LINQ query to run the case statement. Can anyone provide me some code that will allow me to have a case statement inside my chain based on LUIS Intents?
Omg is right in his comment.
Remember that IDialogs have a TYPE, meaning, IDialog can return an object of a type specified by yourself:
public class TodoItemDialog : IDialog<TodoItem>
{
// Somewhere, you'll call this to end the dialog
public async Task FinishAsync(IDialogContext context, IMessageActivity activity)
{
var todoItem = _itemRepository.GetItemByTitle(activity.Text);
context.Done(todoItem);
}
}
the call to context.Done() returns the object that your Dialog was meant to return. Whever you're reading a class declaration for any kind of IDialog
public class TodoItemDialog : LuisDialog<TodoItem>
It helps to read it as:
"TodoItemDialog is a Dialog class that returns a TodoItem when it's done"
Instead of chaining, you can use context.Forward() which basically forwards the same messageActivity to another dialog class..
The difference between context.Forward() and context.Call() is essencially that context.Forward() allows you to forward a messageActivity that is immediately handled by the dialog called, while context.Call() simply starts a new dialog, without handing over anything.
From your "Root" dialog, if you need to use LUIS to determine an intent and return a specific object, you can simply forward the messageActivity to it by using Forward and then handling the result in the specified callback:
await context.Forward(new TodoItemDialog(), AfterTodoItemDialogAsync, messageActivity, CancellationToken.None);
private async Task AfterTodoItemDialogAsync(IDialogContext context, IAwaitable<TodoItem> result)
{
var receivedTodoItem = await result;
// Continue conversation
}
And finally, your LuisDialog class could look something like this:
[Serializable, LuisModel("[ModelID]", "[SubscriptionKey]")]
public class TodoItemDialog : LuisDialog<TodoItem>
{
[LuisIntent("GetTodoItem")]
public async Task GetTodoItem(IDialogContext context, LuisResult result)
{
await context.PostAsync("Working on it, give me a moment...");
result.TryFindEntity("TodoItemText", out EntityRecommendation entity);
if(entity.Score > 0.9)
{
var todoItem = _todoItemRepository.GetByText(entity.Entity);
context.Done(todoItem);
}
}
}
(For brevity, I have no ELSE statements in the example, which is something you of course need to add)

Categories

Resources