Writing to file asynchronously - c#

I'm having a small problem implementing async methods into my code as it is my first time using it. I am having trouble writing to a file, when I make the method synchronous it works fine, however, when using async - nothing happens.
Here is the code I am currently using:
static async Task WriteFile()
{
string path = $#"C:\Users\{Environment.UserName}\FileToWrite.js";
if (File.Exists(path))
{
try
{
string value;
using (StreamReader reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("AsyncTest.Resources.data.txt")))
{
value = await reader.ReadToEndAsync();
}
using (StreamWriter writer = new StreamWriter(path))
{
await writer.WriteAsync(value);
}
}
catch
{
Debug.WriteLine("Error writing to file.");
}
}
}
I am calling this method like this
await WriteFile(path);
Data in Resources.data.txt:
This is a test, the text file should contain this message.
Any help would be greatly appreciated, thank you!

If this is a console application, then I am guessing your Main method is not returning a Task, which means that your application will exit before the task is complete.
The await keyword will return when it acts on an incomplete Task. Usually it will return its own Task that the caller can use to wait until it's complete. But if the method is void, then it can't return anything and nothing is able to wait for it.
So if you have something like this:
public static async void Main() {
await WriteFile(path);
}
Then as soon as the I/O request is made to the file system, WriteFile will return a Task, then the await in Main will return nothing (because Main is void) and your program will end.
If you are using at least C# 7.1, then you can return a Task from Main:
public static async Task Main() {
await WriteFile(path);
}
And .NET knows that it needs to keep the application going until the Task is complete.
If you cannot use C# 7.1, then you are better off using synchronous code.

Change your method to return a condition of success. This will ensure to run through all of the code prior to function exit.
static async Task<bool> WriteFile(string path)
{
if (File.Exists(path))
{
string value = "";
try
{
using (StreamReader reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("AsyncTest.Resources.data.txt")))
{
value = await reader.ReadToEndAsync();
}
}
catch
{
Debug.WriteLine("Error opening resource.");
return false;
}
try
{
using (StreamWriter writer = new StreamWriter(path))
{
await writer.WriteAsync(value);
}
}
catch
{
Debug.WriteLine("Error writing to file.");
return false;
}
return true;
}
Debug.WriteLine("Error file does not exist.");
return false;
}

Related

Does CancellationToken removes file lock?

I have following async function:
private async Task<bool> ValidateFtpAsync()
{
return await Task.Run(
() =>
{
if(File.Exists("settings.xml"))
{
var xs = new XmlSerializer(typeof(Information));
using (var read = new FileStream("settings.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
Information info = (Information)xs.Deserialize(read);
try
{
var DecryptedInfo = FileCryptoDecryptor.ReadEncryptedConfiguration("hakuna.xml.aes", Global_Variables.AppPassword);
string DecryptedFTPPass = EncryDecryptor.Decrypt(DecryptedInfo.FtpPassword, "UltraSecretPasswordNotGonnaSayItToYou");
return General_Functions.isValidConnection(info.HDSynologyIP, info.FtpUsername, DecryptedFTPPass);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
return false;
}
else
{
MessageBox.Show("Missing settings file.");
return false;
}
});
}
You can see it reads data from a file. My question is if i use CancellationToken for async method does it remove file lock when its in using block?
No, cancellation token by itself does not do anything (and in particular does not close files). The task's code needs to check state of the token repeatedly and act accordingly to perform cancellation.
It is unclear where do you plan to use a cancellation token in your case as there is no repeated operations... But since code has properly set with using(…){} statements irrespective where you break the operation the file will be correctly closed in finally block of using (var read = new FileStream(....

Why is this ASP.NET method blocking, when it should be asynchronous?

Given the following method signature in a WCF service:
public string Query(string request)
{
using (Log.BeginTimedOperation("Persist request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
PayloadHelper.PersistPayloadWithPath(request, payloadURI);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
and the corresponding extension methods:
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-{"MyWCFService"}-{$"{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}"}.{"xml"}";
return Path.Combine(logFolder, fileName);
}
public static void PersistPayloadWithPath(string text, string path)
{
var task = WriteFileAsync(path, text);
task.GetAwaiter().GetResult();
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch (Exception ex)
{
Serilog.Log.Error(ex, "WriteFileAsync");
}
}
This code will block, if for instance the file is being interrogated by anti-virus (guess) or IO slowness to the filer.
So here is the great debate, calling a asynchronous method from a synchronous method in ASP.NET. To this day I still don't know if there is a reliable way to create a fire and forget mechanism. It's not that I don't care about the failure, I do, that 'should be' handled by the catch statement and the static Serilog instance.
While writing this version of the post, it dawned on me that maybe one of the problems might be in fact the logger and it's async wrapper around the File Sink.. will test that in a few.
Any help is appreciated with this bugger.
Thank you,
Stephen
UPDATE-ASYNC
public async Task<string> QueryAsync(string request)
{
using (Log.BeginTimedOperation("PersistAsync-Request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
await PayloadHelper.PersistPayloadWithPathAsync(request, payloadURI).ConfigureAwait(false);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-MyWCFService-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}.xml";
return Path.Combine(logFolder, fileName);
}
public static async Task PersistPayloadWithPathAsync(string text, string path)
{
await WriteFileAsync(path, text).ConfigureAwait(false);
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch
{
// ignored
}
}
Still blocking randomly, lick every 20-30 requests
So it's blocking on the new FileStream(). Looking at the source code, the constructor calls a method called Init(), which actually ends up opening the file. So it is doing I/O in the constructor, which really it shouldn't be since you can't await it.
Setting useAsync should make it run async, if it can. But maybe it can't open the file from a network drive asynchronously.
So your best bet is to just run it inside Task.Run() to guarantee it doesn't block.
The following only applies to .NET Core (unfortunately):
You would be better off using File.WriteAllTextAsync, which would actually make your life easier:
await File.WriteAllTextAsync(path, text);
The documentation of that doesn't really explain anything for some reason, but it works the same as File.WriteAllText (it's just async):
Creates a new file, write the contents to the file, and then closes the file. If the target file already exists, it is overwritten.
So, exactly what you're doing in your code, but this way you can await the whole operation (including opening the file).
I think it waits becuse you use
task.GetAwaiter().GetResult();
If you don't need the result, just remove this line. It should work fine.
If you need the result you should make PersistPayloadWithPath function also async.
The problem is your WriteFileAsync method. It actually runs synchronously until it hits the first await (that's how async/await works). I believe it hangs at new FileStream(...).
If you want to just fire-end-forget form synchronous code, this should be enough:
public static void PersistPayloadWithPath(string text, string path)
{
Task.Run(async () => await WriteFileAsync(path, text));
}
The code above should help you when you don't have async alternatives. However as Gabriel Luci suggested in his answer you should go with await File.WriteAllTextAsync(path, text); as it's probably optimized for async work.
You can still use Task.Run(...); with await File.WriteAllTextAsync(path, text); in fire-and-forget scenario.
Just beware that exceptions from inside the task (WriteFileAsync) won't propagate to the calling thread. This won't be the problem in your case since the whole WriteFileAsync method's body is inside try-catch block, where you log the exception.
EDIT
To illustrate how the threads behave in async/await methods, play around with the following example (try all 3 ways the Bar function is ran):
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Main thread: {Thread.CurrentThread.ManagedThreadId}");
Task.Run(async () => await Bar());
// Task.Run(() => Bar());
// Bar();
Console.ReadLine();
}
static async Task Bar()
{
Console.WriteLine($"Bar thread before await: {Thread.CurrentThread.ManagedThreadId}");
await Foo();
Console.WriteLine($"Bar thread after await: {Thread.CurrentThread.ManagedThreadId}");
}
static async Task Foo()
{
Console.WriteLine($"Foo thread before await: {Thread.CurrentThread.ManagedThreadId}");
var c = new WebClient();
var source = await c.DownloadStringTaskAsync("http://google.com");
Console.WriteLine($"Foo thread after await: {Thread.CurrentThread.ManagedThreadId}");
}
}
}
The root caused was determined to be an incorrectly configured StealthAudit, a tool that we use to audit our filers. Once the configuration was adjusted everything works as expected.

Using MessageBox when part of an await has ConfigureAwait false

Reading Stephen Cleary take on not blocking on Async code I write something like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text=json;
}
so far so good, I understand that after the ConfigureAwait the method is going to continue running on a different context after GetStringAsync returns.
but what about if I want to use something like MessageBox (which is UI) like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
can I do this?
Even more complicated, how about this?
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
try{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
catch(Exception ex)
{
MessageBox.Show("An Exception was raised!");
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
Can I do this?
Now, I am thinking perhaps all the message boxes should be called outside GetJsonAync as good design, but my question is can the above thing be done?
can I do this? [use a MessageBox]
Yes, but mainly because it has nothing to do with async/await or threading.
MessageBox.Show() is special, it is a static method and is documented as thread-safe.
You can show a MessageBox from any thread, any time.
So maybe it was the wrong example, but you do have MessageBox in the title.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
try{
... // old context
... await client.GetStringAsync(uri).ConfigureAwait(false);
... // new context
}
catch
{
// this might bomb
someLabel.Text = "An Exception was raised!";
}
}
In this example, there could be code paths where the catch runs on the old and other paths where it runs on the new context.
Bottom line is: you don't know and should assume the worst case.
I would not use a Message Box, as it is very limited, and dated.
Also, Pop up's are annoying.
Use your own user control which enables user interaction the way you intend it.
In the context of Winforms / WPF / (and I guess UWP), only a single thread can manipulate the UI. Other threads can issue work to it via a queue of actions which eventually get invoked.
This architecture prevents other threads from constantly poking at the UI, which can make UX very janky (and thread unsafe).
The only way to communicate with it the UI work queue (in Winforms) is via the System.Windows.Form.Controls.BeginInvoke instance method, found on every form and control.
In your case:
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...).ConfigureAwait(false);
BeginInvoke(UpdateTextBox, json);
}
private void UpdateTextBox(string value)
{
textBox1.Text=json;
}

How to wait for HttpClient GetAsync call until it returns the request in C#

I am trying to get data by the HttpClient. Data vary in size, it could be from few bytes to megabyte. I noticed many times my application exist even before it returns from the GetAsync. How can I wait until GetAsync complete it call? From the main app:-
backup.DoSaveAsync();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Red;
// My app exist by printing this msg, wihout getting any data.
// someitmes it gets data and other times it gets notinng.
// I used sleep to wait to get the call completed.
Console.WriteLine("\nBackup has done successfully in SQL database")
public async void DoSaveAsync()
{
using (var client = GetHttpClient(BaseAddress, path, ApiKey))
{
Stream snapshot = await GetData(client, path);
if (snapshot != Stream.Null)
{
snapshot.Position = 0;
SaveSnapshot(snapshot);
}
}
}
private async Task<Stream> GetData(HttpClient client, string path)
{
HttpResponseMessage response = null;
try
{
response = await client.GetAsync(path);
System.Threading.Thread.Sleep(5000);
if (response.IsSuccessStatusCode == false)
{
Console.WriteLine($"Failed to get snapshot");
return Stream.Null;
}
return await response.Content.ReadAsStreamAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Stream.Null;
}
}
Code update after the comments and answer:
// in my main app, I have this code.
// How can I get the completed task or any error return by the task here.
backup.DoBackupAsync().Wait();
public async Task<Stream> DoSaveAsync()
{
using (var client = GetHttpClient(BaseAddress, SnapshotPath, ApiKey))
{
try
{
Stream snapshot = await GetSnapshot(client, SnapshotPath);
if (snapshot != Stream.Null)
{
snapshot.Position = 0;
SaveSnapshot(snapshot);
}
return snapshot;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
As the method is async, the backup.DoSaveAsync() line only start a Task but doesn't wait for the result, so you may call Console.ReadLine (and possibly exit your program) before the task is completed. You should return Task instead of void - it's generally bad design to have a void async method, and younhave to await backup.DoSaveAsync() either via await (if you call from an async method), either via .Wait().
Also, in case of error in GetData, you don't return any error for DoSaveAsync - you may want to deal with this, in the current code, you would print "Failed to get snapshot" and then "Backup has done successfully in SQL database". Consider to not use Console.ReadLine in GetData and return a Task in DoSaveAsync indicating success
No need to put a thread.sleep here - you already await the result.

Why is MS forcing me into an asynchronous operation?

I need to read a numeric value (a version number) from a text file in my resources.
I compare this version number to the version number of an installed component.
If the version number in the resources is higher than the installed version, I copy the new component (a database) from my resources to a local directory where the user can use it.
I need to do this synchronously because my application can't work without the database.
However, I don't see any way to do it synchronously.
MS forces me to do it with an async task like this:
private async Task<string> ResourcesReadTextFile(string uFileName)
{
string sRet = "";
try
{
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName));
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (streamReader.Peek() >= 0)
{
sRet = streamReader.ReadLine();
}
}
}
catch (Exception ex)
{
Debug.Assert(false);//check here
}
return sRet;
}
Now I've encountered a situation where the app started before the database was copied over to the local directory as the copying also needs to be done asynchronously, there simply isn't any way to do it synchronous.
There is no such function as StorageFile.Copy().
What I'm therefore using is:
private async void pCopyFromResourcesToLocal(string uFileName)
{
// Cant await inside catch, but this works anyway
try
{
StorageFile storfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName));
await storfile.CopyAsync(ApplicationData.Current.LocalFolder);
}
catch (Exception ex)
{
Debug.WriteLine("");
}
}
This drives me crazy.
People write that Async should be embraced and hugged and appreciated, but in my case it causes nothing but trouble.
I don't see any way of making this thing synchronous, and I wonder why MS forces me to do it that way.
Any help very much appreciated.
Thank you.
Code as a screenshot:
Edit: I've added the top methods here:
public static async Task<DB> InitAppDb()
{
IFileHelper helper = DependencyService.Get<IFileHelper>();
string path = await helper.GetFilePathAndCopyFromResourcesIfNotPresent("tablet.db");
return (_dbApp = new DB(path));
}
public async Task CopyDatabaseIfNotExists(string uFileName)
{
IsolatedStorageFile nExpectedFolder = IsolatedStorageFile.GetUserStoreForApplication();
bool bCopyNewDB = false;
Task<bool> datatask = pResourceIsNewer(uFileName);
bCopyNewDB = await datatask;
if (! bCopyNewDB)
{
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync(uFileName); //nExpectedFolder.GetFileAsync(dbPath);/// ApplicationData.Current.LocalFolder.GetFileAsync("preinstalledDB.db");
// No exception means it exists
return;
}
catch (System.IO.FileNotFoundException)
{
// The file obviously doesn't exist
}
}
pCopyFromResourcesToLocal(uFileName);
}
private async Task<bool>pResourceIsNewer(string uPath)
{
string sFileNameAppDBVersion =uPath + ".txt";
if (IsolatedStorageFileExist(sFileNameAppDBVersion))
{
int iAppDBVersionInstalled = Convert.ToInt32(IsolatedStorageReadTextFile(sFileNameAppDBVersion));
Task<string> datatask = ResourcesReadTextFile(sFileNameAppDBVersion);
string s = await datatask;
int iAppDBResources = Convert.ToInt32(s);
bool b = (iAppDBResources > iAppDBVersionInstalled);
return b;
}
else
{
return true;
}
}
All you have to do is:
//private async void pCopyFromResourcesToLocal(string uFileName) { ... }
private async Task pCopyFromResourcesToLocal(string uFileName) { ... }
and then you can await it:
//pCopyFromResourcesToLocal(uFileName);
await pCopyFromResourcesToLocal(uFileName);
and it will all be completed before you call return (_dbApp = new DB(path));
Nothing in this async/await chain can happen out of order.
When you say that your app can't work without the database, remember that using an await keyword does just that, so the following line of code will not execute until after the async call returns. You can structure your code such that it is responsive while you wait for the DB to come back online.
However, you can force your function to be synchronous by using a construct such as:
StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName)).GetAwaiter().GetResult();
Or, even better, have a look at the JoinableTaskFactory.
Any asynchronous API method can be made synchronous by simply tagging .GetAwaiter().GetResult() on the end, as #pm_2 says.
In the case of Task<T> results, you can simply use .Result, and for Task results, .Wait().
You can either write an asynchronous function to read your file, then wait for its result at the top level, or you can write a synchronous method and wait for the result of every call it makes to async functions.
So Microsoft is not forcing you into anything: it's just providing a simpler API with the lowest common denominator of both asynchronous and synchronous workflows.

Categories

Resources