Asynchronous async await method to generate pdf - c#

I decided to convert the method that generates a PDF to asynchronous call. The asynchronous call doesn't produce the requested PDF and I have no idea why this is happening
The client who calls the async operation is as follows:
public QuotationResponse CreateQuotation(IQuotation quotation)
{
...
// Create Quotation PDF
_pdfWriter.GeneratePdfAsync(response);
return response;
}
The class responsible for generating the PDF is the following:
public class Writer
{
//... other class methods
public async Task GeneratePdfAsync(QuotationResponse response)
{
await new Task(() =>
{
var currentPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
var formFile = Path.Combine(currentPath, Settings.Default.QUOTATION_TEMPLATE_PATH);
var newFile = Path.Combine(currentPath, Settings.Default.QUOTATION_PDF_PATH);
var reader = new PdfReader(formFile);
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(newFile, FileMode.Create)))
{
....
// flatten form fields and close document
stamper.FormFlattening = true;
stamper.Close();
}
});
}
}
I suspect that I'm not doing something right with the async - await operation but don't know what. Could you please assist?

You should never use the Task constructor. Ever. It has absolutely no valid use cases. At all. For any kind of approach to any kind of problem. Full details on my blog.
Since there is no asynchronous work to do, you should just use synchronous methods:
public class Writer
{
//... other class methods
public void GeneratePdfAsync(QuotationResponse response)
{
var currentPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
...
}
}
If you want to call this from a GUI application and don't want to block the UI thread, then you can call it on a background thread by using await Task.Run, as such:
QuotationResponse response = await Task.Run(() => CreateQuotation(quotation));

Related

How do I get the "await using" syntax correct?

I have the following synchronous code, which works fine:
private void GenerateExportOutput()
{
using StreamWriter writer = new(Coordinator.OutputDirectory + #"\export.txt");
if (this.WikiPagesToExport.IsEmpty)
{
return;
}
var wanted = new SortedDictionary<string, WikiPage>(this.WikiPagesToExport, StringComparer.Ordinal);
foreach (var title in wanted.Keys)
{
writer.WriteLine(title);
}
}
I want to change it to be asynchronous. So:
private async Task GenerateExportOutputAsync()
{
using StreamWriter writer = new(Coordinator.OutputDirectory + #"\export.txt");
if (this.WikiPagesToExport.IsEmpty)
{
return;
}
var wanted = new SortedDictionary<string, WikiPage>(this.WikiPagesToExport, StringComparer.Ordinal);
foreach (var title in wanted.Keys)
{
await writer.WriteLineAsync(title).ConfigureAwait(false);
}
await writer.FlushAsync().ConfigureAwait(false);
}
Which compiles. But one of the analyzers I use (Meziantou.Analyzer) now suggests that I "prefer using 'await using'". I've never used await using (though I've tried several times in the past and have always run into the same problems I'm running into now). But I would like to use it, so:
await using StreamWriter writer = new StreamWriter(OutputDirectory + #"\export.txt").ConfigureAwait(false);
Now it no longer compiles: CS0029 Cannot implicitly convert type 'System.Runtime.CompilerServices.ConfiguredAsyncDisposable' to 'System.IO.StreamWriter'. OK, fine, so I change it to use var instead:
await using var writer = new StreamWriter(OutputDirectory + #"\export.txt").ConfigureAwait(false);
Which gets it past the CS0029, but now the later code doesn't compile: Error CS1061 'ConfiguredAsyncDisposable' does not contain a definition for 'WriteLineAsync' (and a similar one for FlushAsync. Soooo... maybe cast it?
await ((StreamWriter)writer).WriteLineAsync(title).ConfigureAwait(false);
Nope: Error CS0030 Cannot convert type 'System.Runtime.CompilerServices.ConfiguredAsyncDisposable' to 'System.IO.StreamWriter'
I've googled a bunch and read a bunch, both now and several times in the past, but I just have been completely unable to figure out how to use this "await using" thing. How can I do so? Thanks.
The await using syntax currently (C# 10) leaves a lot to be desired, regarding its support for configuring the awaiting of IAsyncDisposables. The best we can do is this:
private async Task GenerateExportOutputAsync()
{
StreamWriter writer = new(Coordinator.OutputDirectory + #"\export.txt");
await using (writer.ConfigureAwait(false))
{
//...
}
}
...which is not really much more compact than not using the await using syntax at all:
private async Task GenerateExportOutputAsync()
{
StreamWriter writer = new(Coordinator.OutputDirectory + #"\export.txt");
try
{
//...
}
finally { await writer.DisposeAsync().ConfigureAwait(false); }
}
Related GitHub issue: Using ConfigureAwait in "await using" declaration.
It can be done in two lines:
private async Task GenerateExportOutputAsync()
{
StreamWriter writer = new(Coordinator.OutputDirectory + #"\export.txt");
await using var _ = writer.ConfigureAwait(false);
//...
}

Reading/Writing Async Files for Universal App

im trying to Reading/Writing Async Files for an Universal App in c#.
When i write and read a file for first time, it works... But when i retry it immeadiatly, there are two Errors: 1. UnauthorizedAccess 2. Handle with the OPLOCK has been closed
It seems that the methods arent finished yet and so the data is not free
(in my frame is a button which adds a new member to a List, then the list shall serialized in an XML data. When i reNavigate to that page, that XML sheet shall be deserialized back to that List, because the Content shall be displayed)
List<Immobilie> immoListe = new List<Immobilie>();
private const string FileName_ImmoObjects = "ImmoObjects.xml";
StorageFolder sFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
IStorageFile latestImmoListFile;
public Startmenue()
{
this.InitializeComponent();
immoListe.Add(new Immobilie()); // for testing creating an XML first
immoListe[0].adresse = "Foo1";
immoListe.Add(new Immobilie());
immoListe[1].adresse = "Foo2";
WriteImmoListAsync();
ReadImmoListAsync(); // These two steps working
WriteImmoListAsync(); // everything more causes error
ReadImmoListAsync();
}
public async void WriteImmoListAsync()
{
try
{
IStorageFolder folder = await sFolder.CreateFolderAsync("Saves", CreationCollisionOption.OpenIfExists);
latestImmoListFile = await folder.CreateFileAsync(FileName_ImmoObjects, CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await latestImmoListFile.OpenAsync(FileAccessMode.ReadWrite))
using (Stream outputStream = stream.AsStreamForWrite())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Immobilie>));
serializer.WriteObject(outputStream, immoListe);
}
}
catch (Exception e)
{
var d = new MessageDialog(e.ToString());
await d.ShowAsync();
}
}
public async void ReadImmoListAsync()
{
int i = 0;
try
{
IStorageFolder folder = await sFolder.GetFolderAsync("Saves");
i = 1;
latestImmoListFile = await folder.GetFileAsync(FileName_ImmoObjects);
i = 2;
using (IRandomAccessStream stream = await latestImmoListFile.OpenAsync(FileAccessMode.Read))
{
i = 3;
using (Stream inputStream = stream.AsStreamForRead())
{
i = 4;
DataContractSerializer deserializer = new DataContractSerializer(typeof(List<Immobilie>));
i = 5;
immoListe = (List<Immobilie>)deserializer.ReadObject(inputStream);
}
}
}
catch (Exception e)
{
var d = new MessageDialog("Fehler I = " + i + "\n" + e.ToString());
await d.ShowAsync();
}
}
So what can i do and why is it so difficult??(normal I/O is easy-peasy).-.
As I describe in my MSDN article on async best practices, you should avoid async void:
public async Task WriteImmoListAsync();
public async Task ReadImmoListAsync();
Once your methods are properly async Task, then you can await them:
await WriteImmoListAsync();
await ReadImmoListAsync();
await WriteImmoListAsync();
await ReadImmoListAsync();
You can't start the methods again until you wait for them to complete. What that above code is trying to do is to write to a file, but while that's processing, it tries to open the file and write to it while the first method call hasn't completed. You need to wait for those method calls to finish before running them again - using the await keyword would be helpful here
It might be that the process writing/reading the file are still attached to the file. You might want to take a look at this pattern for async file read/write from Microsoft:
https://msdn.microsoft.com/en-ca/library/mt674879.aspx
Also, note that if the read and write are done from differents process, you're going to have to use a mutex. Here's a great explanation on how it works:
What is a good pattern for using a Global Mutex in C#?

Read a file from background task

I'm trying to call a method from inside the Run method of a background task which among other it desirializes a xml file. The problem is that I end up in a deadlock. This is the methos that reads the file
protected async Task<Anniversaries> readFile(string fileName)
{
IStorageFile file;
Anniversaries tempAnniversaries;
file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
using (IRandomAccessStream stream =
await file.OpenAsync(FileAccessMode.Read))
using (Stream inputStream = stream.AsStreamForRead())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Anniversaries));
tempAnniversaries = serializer.ReadObject(inputStream) as Anniversaries;
}
return tempAnniversaries;
}
and here is the Run method
public sealed class TileUpdater : IBackgroundTask
{
GeneralAnniversariesManager generalManager = new GeneralAnniversariesManager();
Anniversaries tempAnn = new Anniversaries();
string test = "skata";
public async void Run(IBackgroundTaskInstance taskInstance)
{
DateTime curentTime = new DateTime();
var defferal = taskInstance.GetDeferral();
await generalManager.InitializeAnniversariesAsync().AsAsyncAction();
curentTime = DateTime.Now;
var updater = TileUpdateManager.CreateTileUpdaterForApplication();
updater.EnableNotificationQueue(true);
updater.Clear();
for (int i = 1; i < 6; i++)
{
var tile = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150BlockAndText01);
tile.GetElementsByTagName("text")[0].InnerText = test + i;
tile.GetElementsByTagName("text")[1].InnerText = curentTime.ToString();
updater.Update(new TileNotification(tile));
}
defferal.Complete();
}
I'm assuming that by deadlock you mean that the deserialization method finishes too late and your original program tries to read the data before it's finished loading.
It depends on how complicated/reliable you want your solution to be and how you're intending to use the program. The simplest way relies on the fact that the directory creation function is always 100% atomic in Windows/Unix and OSX. For example at the top of your readFile function have something like this.
Directory.CreateDirectory("lock");
Before you start parsing the results of your async action in TileUpdater, have a loop that looks like this.
while (Directory.Exists("lock"))
{
Thread.Sleep(50);
}
This assumes that everything is happening in the same directory, generally you'll want to replace "lock" with a path that leads to the user's temp directory for their version of Windows/Linux/OSX.
If you want to implement something more complicated where you're reading from a series of files while at the same time reading the deserialized output into your class, you'll want to use something like a System.Collections.Concurrent.ConcurrentQueue that allows your threads to act completely independently without blocking each other.
Incidentally I'm assuming that you know that the class Process and the function .waitfor() exists. You can spin off a thread and then at a later point, halt the main thread until the spawned thread finishes.
Actually I think I've found where the problem is. At the namespaces, I've tried a try and catch and I got an exception about using different namespaces at the datacontract serealizer. I have updated the code like this
file = await ApplicationData.Current.LocalFolder.GetFileAsync("EortologioMovingEntries.xml");
try
{
using (IRandomAccessStream stream =
await file.OpenAsync(FileAccessMode.Read))
using (Stream inputStream = stream.AsStreamForRead())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Anniversaries), "Anniversaries", "http://schemas.datacontract.org/2004/07/Eortologio.Model");
tempAnniversaries = serializer.ReadObject(inputStream) as Anniversaries;
}
}
catch (Exception ex)
{
error = ex.ToString();
tempAnniversaries.Entries.Add(new AnniversaryEntry("Ena", DateTime.Now, "skata", PriorityEnum.High));
}
I don't get any exceptions now but the tempAnniversaries returns null. Any ideas?

Creating Asynchronous Methods with Task Factory and Callback

I am starting to create some classes who will fire asynchronous operations and I want the client to register a callback to receive some results. Finally I reached the following code. It is just an example, and I would like to know if there is a better way of doing it using TaskFactory and Action<>, Func<>
Here is the client basic example:
Client client2 = new Client();
client2.GetClientList(ClientCallBack);
private static void ClientCallBack(List<Client> listado)
{
//Receive the list result and do some stuff in UI
}
Here is the Client class GetCLientList asynchronous example:
public void GetClientList(Action<List<Client>> Callback)
{
List<Client> listado=null;
Task.Factory.StartNew(() =>
{
listado = new List<Client>{
new Client{ apellidos="Landeras",nombre="Carlos",edad=25},
new Client{ apellidos="Lopez", nombre="Pepe", edad=22},
new Client{ apellidos="Estevez", nombre="Alberto", edad=28}
};
//Thread.Sleep to simulate some load
System.Threading.Thread.Sleep(4000);
}).ContinueWith((prevTask) =>
{
Callback(listado);
}
);
}
Is there a better way of doing it?. I know I can return Task from my function and register the continueWith in client side, but I want to wrap it inside the class.
EDIT
I'm posting another example. I was trying to make sync/async versions of a webrequest.
Is this approach correct?:
public string ExecuteRequest(string url)
{
HttpWebRequest httpwebrequest = (HttpWebRequest) WebRequest.Create(url);
HttpWebResponse httpwebresponse = (HttpWebResponse) httpwebrequest.GetResponse();
using (StreamReader sr = new StreamReader(httpwebresponse.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
public void ExecuteRequestAsync(string uri,Action<string> callbackResult)
{
Task.Factory.StartNew(() => ExecuteRequest(uri), CancellationToken.None)
.ContinueWith((task) => callbackResult(task.Result));
}
First, your method doesn't seem to be actually asynchronous, so you're not going to gain much from making it look like one. If the user of your method decides to run it on another thread, they can do it themselves.
Second, if you can use C# 5.0, you should follow the Task-based asynchronous pattern, to make your method easier to use. With that (and assuming you have a reason to ignore my first point above), your code could look like this:
public Task<List<Client>> GetClientListAsync()
{
return Task.Run(() =>
{
var list = new List<Client>
{
new Client { apellidos="Landeras", nombre="Carlos", edad=25 },
new Client { apellidos="Lopez", nombre="Pepe", edad=22 },
new Client { apellidos="Estevez", nombre="Alberto", edad=28 }
};
//Thread.Sleep to simulate some load
System.Threading.Thread.Sleep(4000);
return list;
});
}
Or, if you wanted to make your code actually asynchronous:
public async Task<List<Client>> GetClientListAsync()
{
var list = new List<Client>
{
new Client { apellidos="Landeras", nombre="Carlos", edad=25 },
new Client { apellidos="Lopez", nombre="Pepe", edad=22 },
new Client { apellidos="Estevez", nombre="Alberto", edad=28 }
};
//Task.Delay() to simulate some load
await Task.Delay(4000);
return list;
}
In both cases, you could then use this method without using callbacks from an async method like this:
var client = new Client();
var list = await client.GetClientListAsync();
//Receive the list result and do some stuff in UI
Third, if you didn't want to (or couldn't) use async-await, then your code is close, but quite right. The problem is that the callback won't actually execute on the UI thread. For that, you would need to use TaskScheduler.FromCurrentSynchronizationContext().
Also, your design where Client has a GetClientList() method seems suspicious to me. Such method probably belongs to some sort of repository object, not to Client.

"await" doesn't wait for the completion of call

I'm building a Metro App.
In the MainPage.xaml.cs, I instantiate Album as follows:
Album album = new Album(2012); //With the album ID as its parameter.
ListView1.ItemsSource = album.Songs;
In the Album.cs, the constructor is as follows:
public Album(int ID)
{
this.ID = ID;
Initialize(); //Serves as a wrapper because I have to call httpClient.GetStreamAsync() and "async" doesn't work for the constructor.
}
Finally, the Initialize method:
private async void Initialize()
{
//...some code...
HttpClient cli = new HttpClient();
Stream SourceStream = await HttpClient.GetStreamAsync("http://contoso.com");
//...some code...
this.Songs = Parse(SourceStream);
}
The problem is when it runs to GetStreamAsync, it then goes to ListView1.ItemsSource = album.Songs directly with the album.Songs null.
Is there any quick solution to this problem?
Yes. The whole point of async and await are that you don't block. Instead, if you're "awaiting" an operation which hasn't completed yet, a continuation is scheduled to execute the rest of the async method, and control is returned to the caller.
Now because your method has a type of void, you have no way of knowing when that's even finished - if you returned Task (which wouldn't require any change in the body of the method) you'd at least be able to work out when it had finished.
It's not really clear what your code looks like, but fundamentally you should only be trying to set the ItemsSource after initialization has finished. You should probably have your MainPage code in an async method too, which would look something like:
Album album = new Album(2012);
ListView1.ItemsSource = await album.GetSongsAsync();
Your GetSongs() call would then be:
private async Task<List<Song>> GetSongsAsync()
{
//...some code...
HttpClient cli = new HttpClient();
Stream SourceStream = await HttpClient.GetStreamAsync("http://contoso.com");
//...some code...
return Parse(SourceStream);
}
This means Songs would no longer be a property of Album itself, although you could add it in for caching purposes if you wanted to.
Make Songs property return Task<List<Song>> and await at ListView1.ItemsSource = await album.Songs;

Categories