Cannot asynchronously open files inside class constructor - c#

I'm pretty new to C#. I'm trying to open a pair of CSV text files using the StorageFile class, but I need to do it inside a class constructor (which cannot be async):
private async Task SetupFileAccess()
{
MainCarDataStorageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///leaf.csv"));
var MainCarDataInputStream = await MainCarDataStorageFile.OpenReadAsync();
var MainCarDataClassicStream = MainCarDataInputStream.AsStreamForRead();
MainCarDataStream = new StreamReader(MainCarDataClassicStream);
await MainCarDataStream.ReadLineAsync(); //clear header line from csv
LookupTableStorageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Full_Charge_To_Empty.csv"));
var LookupTableInputStream = await LookupTableStorageFile.OpenReadAsync();
var LookupTableClassicStream = LookupTableInputStream.AsStreamForRead();
LookupTableDataStream = new StreamReader(LookupTableClassicStream);
await LookupTableDataStream.ReadLineAsync(); //clear header line from csv
}
Then I call Wait() on that function to block execution.
The problem is when I reach the second OpenReadAsync() function call to open a stream to the second file (Full_Charge_to_Empty.csv), the program just churns forever. No crash, no progress.
It seems I can only access the first file opened. If I comment out the first File access (leaf.csv), I am able to access the second file. If I switch their positions (leaf.csv is opened 2nd), I cannot open leaf.csv.
I feel like there must be something I don't understand about how these file accesses work...is it because my accessing of the first file (leaf.csv) is occupying a resource of some type? Appreciate the help!

The problem is that you're trying to force async functions to complete synchronously inside a constructor; that's not going to end well. The correct approach is to use an async factory method to front-load the work and then return a populated object. For example:
class Test
{
// Make constructor private so no-one can create it
private Test()
{ }
// Simple string properties that can't be async
public string Text1 { get; private set; }
public string Text2 { get; private set; }
// Async factory method that can do async things
public static async Task<Test> CreateAsync()
{
var result = new Test();
result.Text1 = await LoadFile("file1.txt");
result.Text2 = await LoadFile("file2.txt");
return result;
}
// Helper function
static async Task<string> LoadFile(string filename)
{
var file = await Package.Current.InstalledLocation.GetFileAsync(filename);
var size = (await file.GetBasicPropertiesAsync()).Size;
var reader = new DataReader(await file.OpenReadAsync());
reader.UnicodeEncoding = UnicodeEncoding.Utf8;
await reader.LoadAsync((uint)size);
// assumes single-byte UTF codepoints, eg ASCII
return reader.ReadString((uint)size);
}
}
Using it:
var test = await Test.CreateAsync();
await new MessageDialog($"{test.Text1}{Environment.NewLine}{test.Text2}").ShowAsync();
return;

Related

How to create a task that watches the size of a file in a c# console application

I'm working on a C# project that will need to create text files and fill them with guids using an array of 25 tasks. I want to use another task separate from the writing tasks to monitor the size of the file at 0.5 second intervals.
I'm struggling to figure out a way to do this. I know with a C# WPF application I could use the FILEIO class but I don't think I have access to that using a console app.
What could I use instead of the FILEIO class to create this task and how would I create it?
you can create a new Thread and use the Length property of a System.IO.FileInfo object to get the size of a file.
See this link for more details on Threading.
How about something like this. Everything is asynchronous. The only thread created monitors the count (so that you can see what's going on - which is the point of this, I think).
First I create a class like this:
internal class GuidWriter
{
public string Folder { get; }
public string FileNamePattern { get; }
public int CountOfFiles { get; }
public int CountOfGuids { get; }
public int TotalGuidsToWrite { get; }
private int _currentGuidCount = 0;
public int CurrentGuidCount { get { return _currentGuidCount; } }
public GuidWriter(string folder, string fileNamePattern, int countOfFiles, int countOfGuids)
{
Folder = folder;
FileNamePattern = fileNamePattern;
CountOfFiles = countOfFiles;
CountOfGuids = countOfGuids;
TotalGuidsToWrite = countOfFiles * countOfGuids;
}
public async Task WriteAllFiles()
{
var writeTaskList = new List<Task>(CountOfFiles);
for (int i = 0; i < CountOfFiles; i++)
{
var writeTask = WriteGuidsToOneFile(i + 1);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
private async Task WriteGuidsToOneFile(int fileNumber)
{
var fileName = string.Format(FileNamePattern, fileNumber);
var filePathName = Path.Combine(Folder, fileName);
using (StreamWriter writer = File.CreateText(filePathName))
{
for (var i = 0; i < CountOfGuids; ++i)
{
var guidString = Guid.NewGuid().ToString();
await writer.WriteLineAsync(guidString);
Interlocked.Increment(ref _currentGuidCount);
}
}
}
}
You create one of these things, tell it how it should work (in the constructor) and then let it rip in the WriteAllFiles (which is async, and which you should await).
It uses Interlocked.Increment on the Guid count - if the framework doesn't anything in threads, I want to make sure that I don't mess up the count.
Then in the main program, I create a Monitor function that counts the number of Guids created and outputs them to the console. It runs in a separate thread, and will go away when it's CancellationToken is canceled:
public static async Task MonitorGuidProduction (GuidWriter guidWriter, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var monitorString = $"{guidWriter.CurrentGuidCount}/{guidWriter.TotalGuidsToWrite}";
Console.WriteLine(monitorString);
await Task.Delay(500);
}
}
Then, in the main program, I put it all together:
static async Task Main(string[] args)
{
using (var cancelTokenSource = new CancellationTokenSource())
{
var cancelToken = cancelTokenSource.Token;
var folder = #"C:\temp\guids";
var pattern = "GuidFile{0}.txt";
var guidWriter = new GuidWriter(folder, pattern, CountOfFiles, CountOfGuids);
var monitorTask = Task.Run(() => MonitorGuidProduction(guidWriter, cancelToken));
cancelToken));
await guidWriter.WriteAllFiles();
cancelTokenSource.Cancel();
await monitorTask;
Console.WriteLine($"{guidWriter.CurrentGuidCount} Guids written to {CountOfFiles} files");
Console.ReadLine();
}
}
I create an instance of a GuidWriter and, just before I let it go (by calling guidWriter.WriteAllFiles, I start up my monitor function in another thread. I do that by running Task.Run, but not awaiting its result until everything else is done (getting the cleanup just right).
Then I await the call to guidWriter.WriteAllFiles, and when it completes, I signal my cancellation token to stop the monitor task/thread.

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.

Calling Async in a Sync method

I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.

await seems to quietly return

I have a method that calls await on an async method.
private async void LoadPic(string imageFileName)
{
StorageFolder sf = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFolder subFolder = await sf.GetFolderAsync("Assets");
StorageFile sfile = await subFolder.GetFileAsync(imageFileName);
When I set a break point on the very next line, the break point is never hit. It just drops out of the method and returns.
public PixelData GrabPixelData(string imageFileName)
{
if (!ImageDictionary.ContainsKey(imageFileName))
{
// doesn't exist yet, so load it
LoadPic(imageFileName);
}
PixelData pd = new PixelData();
bool found = false;
while( found == false ) {
found = ImageDictionary.TryGetValue(imageFileName, out pd);
}
return pd;
// throw new NullReferenceException();
}
After I call LoadPic() I added a loop that keeps checking to see if it added the image in this file to a Dictionary. It never seems to get added and just hangs.
This method worked fine on a child class that I abstracted it out of.
Edit:
Modified a couple things. Everything seems to work now, but when I assign the results to the child class I get a null exception error (even though the debugger doesn't indicate anything is null!).
I wonder if it has to do with the fact that it is wrapped in a Task.
Child class:
private async void LoadPic()
{
// I realize the async void is to be avoided, but ... I'm not sure because there isn't anything I want it to return. Should it just return a dummy task?
sat1.pixelData = await rootPage.GrabPixelData("sat1.png");
LoadPic:
private async Task<PixelData> LoadPic(string imageFileName)
{
StorageFolder sf = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFolder subFolder = await sf.GetFolderAsync("Assets");
StorageFile sfile = await subFolder.GetFileAsync(imageFileName);
...
return pd;
GrabPixelData:
public async Task<PixelData> GrabPixelData(string imageFileName)
{
if (!ImageDictionary.ContainsKey(imageFileName))
{
// doesn't exist yet, so load it
PixelData pd = await LoadPic(imageFileName);
ImageDictionary.Add(imageFileName, pd);
}
var test = ImageDictionary[imageFileName];
return ImageDictionary[imageFileName];
}
You should avoid async void. Also, polling shared state to detect completion of an asynchronous operation is a no-no. That kind of CPU usage can get your app rejected from the Windows Store.
I recommend you change LoadPic to return Task<PixelData>. Then change your ImageDictionary from Dictionary<string, PixelData> to Dictionary<string, Task<PixelData>>. Your GrabPixelData method can then look like this:
public Task<PixelData> GrabPixelData(string imageFileName)
{
if (!ImageDictionary.ContainsKey(imageFileName))
{
// doesn't exist yet, so load it
ImageDictionary.Add(imageFileName, LoadPic(imageFileName));
}
return ImageDictionary[imageFileName];
}

Task continue with multiple tasks

I have a method that uploads one file to server. Right now is working besides any bad coding on the method (Im new to Task library).
Here is the code that uploads a file to server:
private async void UploadDocument()
{
var someTask = await Task.Run<bool>(() =>
{
// open input stream
using (System.IO.FileStream stream = new System.IO.FileStream(_cloudDocuments[0].FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (StreamWithProgress uploadStreamWithProgress = new StreamWithProgress(stream))
{
uploadStreamWithProgress.ProgressChanged += uploadStreamWithProgress_ProgressChanged;
// start service client
SiiaSoft.Data.FieTransferWCF ws = new Data.FieTransferWCF();
// upload file
ws.UploadFile(_cloudDocuments[0].FileName, (long)_cloudDocuments[0].Size, uploadStreamWithProgress);
// close service client
ws.Close();
}
}
return true;
});
}
Then I have a ListBox where I can drag & drop multiple files so what I want to do is to do a FOR LOOP within the ListBox files and then call UploadDocument(); but I want to first upload 1st file in the listBox then when completed continue with the second file and so on...
Any clue on the best way to do it?
Thanks a lot.
You should make your UploadDocument return Task. Then you can await the task in a loop. For example:
private async Task UploadAllDocuments()
{
string[] documents = ...; // Fetch the document names
foreach (string document in documents)
{
await UploadDocument(document);
}
}
private async Task UploadDocument(string document)
{
// Code as before, but use document instead of _cloudDocuments[0]
}
In fact, your UploadDocument can be made simpler anyway:
private Task UploadDocument()
{
return Task.Run<bool>(() =>
{
// Code as before
});
}
Wrapping that in an async method isn't particularly useful.
(You may well want to change the type to not be string - it's not clear what _cloudDocuments is.)
In general, you should always make an async method return Task or Task<T> unless you have to make it return void to comply with an event-handling pattern.

Categories

Resources