Search code examples
c#asynchronousxamarinvisual-studio-2017storagefile

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:

enter image description here

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;
        }
    }

Solution

  • 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.