I have an action which retrieves a file from a SQL database, creates a temporary physical file, and depending on the type of the file either attempts to extract a thumbnail using the ShellFile API, or if it's a PDF, convert the 1st page to a jpg using ImageMagick.
This action is called using an Ajax GET request which receives a base64 representation of the image which is displayed to the user.
Currently, if lets say 5 Ajax requests were made, the server will process each request one at a time. Instead of this behavior, I'd prefer if it processed multiple requests at a time so the user can receive all of the information faster.
So far I've tried making the function use async/await, but it still processes the requests one at a time. Since the APIs I'm using are older and don't support async IO operations, I was following this guide from Microsoft which explains how you can wrap synchronous functions around async functions. The code I have so far is:
public ApiResult<string> RunTask(string fileName, string guid)
{
using (MagickImage image = new MagickImage())
{
using (MemoryStream stream = new MemoryStream())
{
DbFile file = SharedData.Files.GetFileData(Guid.Parse(guid));
file.ShouldNotBeNull();
string path = Server.MapPath($"~/Temp/{fileName}");
System.IO.File.WriteAllBytes(path, file.FileData);
switch (Path.GetExtension(fileName))
{
case ".pdf":
image.Read(path);
break;
default:
ShellFile shellFile = ShellFile.FromFilePath(path);
Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;
shellThumb.MakeTransparent(shellThumb.GetPixel(0, 0));
ImageConverter converter = new ImageConverter();
image.Read((byte[])converter.ConvertTo(shellThumb, typeof(byte[])));
break;
}
image.Format = MagickFormat.Png;
image.Write(stream, MagickFormat.Png);
System.IO.File.Delete(path);
return Ok<string>($"data:image/png;base64,{Convert.ToBase64String(stream.ToArray())}");
}
}
}
[HttpGet]
public async Task<ApiResult<string>> GenerateThumbnail(string fileName, string guid)
{
return await Task.Run(() => RunTask(fileName, guid));
}
But when debugging it and having breakpoints print when the start and end of the function are reached, I receive:
START: "file1.pdf"
END: "file1.pdf"
START: "file2.jpg"
END: "file2.jpg"
...
When I'd expect something more akin to:
START: "file1.pdf"
START: "file2.jpg"
...
END: "file1.pdf"
END: "file2.jpg"
How would I fix my code to have the desired behavior? If the function is wrapped by an async one, shouldn't it be running multiple calls together? This mostly comes down to not being very familiar with C# and ASP.net backends. Considering the article from Microsoft's docs that I linked says you generally shouldn't wrap synchronous functions around async ones, I have a feeling there is a very different approach I should take.
(I've also verified the Ajax calls are going off together, and it isn't the source of the bottleneck)