Use cancellation token to cancel execution of multiple tasks executed using Task.WhenAll<tasks> method in asp.net core 3.1 - asp.net-core

I am working on an asp.net core application which has downloading files functionality. Using this function we can create multiple download tasks and execute them at once using Task.Whenall.
I am trying to implement cancel download functionality using which we can abort/cancel the complete download operation by clicking a cancel button on one of my razor pages (if I selected 100 files to download and clicked cancel after downloading 10 files, 90 remaining files should not be downloaded).
Currently it cannot be cancelled, once the download operation is started it continuously executes in background even if we close the download page unless all files fail/succeed to download. Exiting the application stops it.
Implementation is as follows.
DownloadsController class:
//variable declaration
CancellationTokenSource cts;
[HttpPost]
public async Task<IActionResult> Download_iles(DownloadFilesModel downloadFilesModel)
{
cts=new CancellationTokenSource();
var successFiles = await _downloadManager.DownloadAsyncpa(downloadFilesModel.param1, downloadFilesModel.param2, cts.Token);
}
ManageFileDownlods class:
public class ManageFileDownlods : BackgroundService, IManageFileDownlods
{
//Constructor
public ManageFileDownlods(IDownloadmaster downloadmaster)
{
_downloadmaster = downloadmaster;
}
public async Task<List<DownloadFile>>
DownloadAsync (funcparam1,funcparam2,CancellationToken cancellationToken=default)
{
// For each server in serverQueue these multiple tasks will execure
while (serverQueue.Count > 0)
{
//Multiple tasks created to downloadfiles
downloadTasksList.Add(ServerProcess(funcparam1, funcparam2, cancellationToken));
//Multiple tasks executed here to download files
try
{
await Task.WhenAll(downloadTasksList.ToArray());
}
catch()
{ }
}
}
private async Task<List<DownloadFile>> ServerProcess (funcparam1, funcparam2,
CancellationToken cancellationToken)
{
while (funcparam1.Count > 0)
{
//5 taks created in loop
for (int i = 0; i < 5; i++)
{
//Multiple tasks created to downloadfiles
fileDownlodTasks.Add(_downloadmaster.Download(param1, param2,
cancellationToken));
await Task.Delay(300);
}
try
{
//Multiple tasks executed here to download files
await Task.WhenAll(fileDownlodTasks.ToArray());
}
catch (TaskCanceledException ex)
{
Debug.WriteLine("execution stopped");
throw ex;
}
}
}
}
Downloadmaster Class:
public async Task<DownloadFile> Download (param1,param2,CancellationToken cancellationToken)
{
//actual function which initiated file download from server
var filecontents = DownloadFileFromServer (param1,param2, cancellationToken);
}
I've spent much time on internet, gone through a lot of different articles over cancellation of tasks, tried to implement multiple approaches given in these articles, but unable to cancel the operation.

Probably, you should add CancellationToken to the action parameters. ASP.NET MVC and ASP.NET Core map that parameter to HttpContext.RequestAborted token. More details here

Related

ASP.NET Core: global handling for showing exception message in current page

In my application I have a set of errors that are "expected" and thus they shouldn't redirect the user to a full error page when they occur. For example, I would like to show the error message in a red box above the current page.
I can easily catch an error on a model action and show the message on the current view, but I was wondering how to do it globally.
I tried a custom middleware with TempData and a filter with ModelState, but can't wrap my head around how to actually pass the error data back to the current page.
This works for a single model (setting the error data in TempData):
public async Task<IActionResult> OnPost() {
try {
// methodThatThrows
}
catch (ApplicationError e) {
TempData["Error"] = e.Message;
return RedirectToPage("Current_Page");
}
return RedirectToPage("Other_Page");
}
For some reason, this doesn't work (in a global middleware), as TempData is empty when the redirect completes. Also setting the middleware doesn't really work with showing the other, more critical errors in the normal error page:
public class ApplicationErrorMiddleware {
private readonly RequestDelegate _next;
private readonly ITempDataDictionaryFactory _tempFactory;
public ApplicationErrorMiddleware(RequestDelegate next, ITempDataDictionaryFactory tempFactory) {
_next = next;
_tempFactory = tempFactory;
}
public async Task InvokeAsync(HttpContext httpContext) {
try {
await _next(httpContext);
}
catch (ApplicationError ex) {
HandleError(httpContext, ex);
}
}
private void HandleError(HttpContext context, ApplicationError error) {
var tempData = _tempFactory.GetTempData(context);
tempData.Add("Error", error.Message);
context.Response.Redirect(context.Request.Path);
}
}
By the tip of #hiiru, I went through a wild goose chase through the configuration options to find a working solution.
My issue was a missing call from the middleware HandleError-method:
private void HandleError(HttpContext context, ApplicationError error) {
var tempData = _tempFactory.GetTempData(context);
tempData.Add("Error", error.Message);
tempData.Save(); // this call was missing
context.Response.Redirect(context.Request.Path);
}
After popping that in there, the tempdata is sent with the redirection back to the original page. Note that this is using the default cookie-based temp data, so no specific configuration is needed.
Now, this works, but it might not be the best way to do this.

Return thread to ThreadPool on lock

When I lock on a thread on the ThreadPool like this the thread is blocked:
private static object _testServerLock = new object();
private static TestServer _testServer = null;
public TestServer GetServer()
{
lock (_testServerLock)
{
if (_testServer == null)
{
_testServer = new TestServer(); // does some async stuff internally
}
}
return _testServer;
}
If I have too more concurrent threads calling this than I have threads in the ThreadPool all of them will end up waiting for the lock, while async code happening elsewhere can't continue since it is waiting for a free thread in the ThreadPool.
So I don't want to block the thread, I need to return it to the ThreadPool while I am waiting.
Is there some other way to lock which returns the waiting thread to the ThreadPool?
Whatever has to be done inside a lock should be moved into a Task, which is started before the tests and finishes, when it has created its resource.
Whenever a test wants to get the resource created by the task, it can block with an await on the creator-task before accessing the resource. So all accesses to the resource are in tasks and can't block all threads of the pool.
Something like:
private static object _testServerLock = new object();
private static TestServer _testServer = null;
private static Task _testTask = null;
private async Task<TestServer> CreateTestServerAsync()
{
...
}
// Constructor of the fixture
public TestFixture()
{
// The lock here may be ok, because it's before all the async stuff
// and it doesn't wait for something inside
lock (_testServerLock)
{
if (_testTask == null)
{
_testTask = Task.Run(async () => {
// it's better to expose the async nature of the call
_testServer = await CreateTestServerAsync();
});
// or just, whatever works
//_testTask = Task.Run(() => {
// _testServer = new TestServer();
//});
}
}
}
public async Task<TestServer> GetServerAsync()
{
await _testTask;
return _testServer;
}
Update:
You can remove the lock using the initialization of the static member.
private static TestServer _testServer = null;
private static Task _testTask = Task.Run(async () => {
_testServer = await CreateTestServerAsync();
});
private static async Task<TestServer> CreateTestServerAsync()
{
...
}
public TestFixture()
{
}
public async Task<TestServer> GetServerAsync()
{
await _testTask;
return _testServer;
}
With xUnit ~1.7+, the main thing you can do is make your Test Method return Task<T> and then use async/await which will limit your hard-blocking/occupation of threads
xUnit 2.0 + has parallel execution and a mechanism for controlling access to state to be shared among tests. Note however that this fundamentally operates by running one tests in the Test Class at a time and giving the Class Fixture to one at a time (which is equivalent to what normally happens - only one Test Method per class runs at a time). (If you use a Collection Fixture, effectively all the Test Classes in the collection become a single Test Class).
Finally, xUnit 2 offers switches for controlling whether or not:
Assemblies run in parallel with other [Assemblies]
Test Collections/Test Classes run in parallel with others
Both of the prev
You should be able to manage your issue by not hiding the asyncness as you've done and instead either exposing it to the Test Method or by doing build up/teardown via IAsyncLifetime

How can I successfully use HttpClient to access iTunes search service in MVC4 app

I am writing an MVC4 app, and in the controller, I need to be able to make a call to a web service to access the iTunes search service in order to get some cover art for a DVD.
I have the following very simple code
public static string GetAlbumArt(Movie movie) {
Task<string> lookupTask = LookupAlbumArt(movie.Title, movie.Year);
lookupTask.Wait(5000);
if (lookupTask.IsCompleted)
{
return lookupTask.Result;
}
else
{
return Timeout;
}
}
private static async Task<string> LookupAlbumArt(string title, int year)
{
using (var client = new HttpClient())
{
string response= await client.GetStringAsync(
"http://itunes.apple.com/search?term=hoosiers&media=movie"
);
return response;
}
}
When I run this and set breakpoints at the return Timeout line in GetAlbumArt and also at the return response line in LookupAlbumArt, the Timeout breakpoint is hit first, and then the LookupAlbumArt return statement is hit, and the correct content is in the response variable.
If I remove the timeout parameter from lookupTask.Wait(5000), the wait GetStringAsync call never returns, and the page never loads.
This feels like a pretty straightforward task, and I'm stumped as to what I'm doing wrong.

When calling a WCF RIA Service method using Invoke, does the return type affect when the Completed callback is executed?

I inherited a Silverlight 5 application. On the server side, it has a DomainContext (service) with a method marked as
[Invoke]
public void DoIt
{
do stuff for 10 seconds here
}
On the client side, it has a ViewModel method containing this:
var q = Context.DoIt(0);
var x=1; var y=2;
q.Completed += (a,b) => DoMore(x,y);
My 2 questions are
1) has DoIt already been activated by the time I attach q.Completed, and
2) does the return type (void) enter into the timing at all?
Now, I know there's another way to call DoIt, namely:
var q = Context.DoIt(0,myCallback);
This leads me to think the two ways of making the call are mutually exclusive.
Although DoIt() is executed on a remote computer, it is best to attach Completed event handler immediately. Otherwise, when the process completes, you might miss out on the callback.
You are correct. The two ways of calling DoIt are mutually exclusive.
If you have complicated logic, you may want to consider using the Bcl Async library. See this blog post.
Using async, your code will look like this:
// Note: you will need the OperationExtensions helper
public async void CallDoItAndDosomething()
{
this.BusyIndicator.IsBusy = true;
await context.DoIt(0).AsTask();
this.BusyIndicator.IsBusy = false;
}
public static class OperationExtensions
{
public static Task<T> AsTask<T>(this T operation)
where T : OperationBase
{
TaskCompletionSource<T> tcs =
new TaskCompletionSource<T>(operation.UserState);
operation.Completed += (sender, e) =>
{
if (operation.HasError && !operation.IsErrorHandled)
{
tcs.TrySetException(operation.Error);
operation.MarkErrorAsHandled();
}
else if (operation.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(operation);
}
};
return tcs.Task;
}
}

Async await in mvvm silverlight 4

In my silverlight mvvm application i am using wcf service to fill listbox which is taking time to load so i need to use async and await in that. how can i use it in the bellow code.
my code in view model:
private void GetLanguage()
{
ServiceAgent.GetLanguage((s, e) =>Language = e.Result);
}
my code in service agent
public void GetLanguage(EventHandler<languageCompletedEventArgs> callback)
{
_Proxy.languageCompleted += callback;
_Proxy.languageAsync();
}
Can anyone help me
You must use TaskCompletionSource to convert EAP (event asynchronous model) to TAP (task asynchronous model). First, add new method to your ServiceAgent (you can create this even as an extension method):
public Task<string> GetLanguageAsync(EventHandler<languageCompletedEventArgs> callback)
{
var tcs = new TaskCompletionSource<string>();
EventHandler<languageCompletedEventArgs> callback;
callback = (sender, e) =>
{
_Proxy.languageCompleted -= callback;
tcs.TrySetResult(e.Result);
};
_Proxy.languageCompleted += callback;
_Proxy.languageAsync();
return tcs.Task;
}
TCS will create a task which you can await then. By using the existing model, it will bridge the gap and make it consumable with async/await. You can now consume it in the view model:
private void GetLanguage()
{
Language = await ServiceAgent.GetLanguageAsync();
}
You can achieve using async and await in Silverlight 5 (or .NET 4) by using this library: AsyncTargetingPack. AsyncTargetingPack is on NuGet.
For a complete walkthrough, read this excellent article:
Using async and await in Silverlight 5 and .NET 4 in Visual Studio 11 with the async targeting pack