Почему MS заставляет меня работать в асинхронном режиме?
Мне нужно прочитать числовое значение (номер версии) из текстового файла в моих ресурсах. Я сравниваю этот номер версии с номером версии установленного компонента. Если номер версии в ресурсах превышает установленную версию, я копирую новый компонент (базу данных) из моих ресурсов в локальный каталог, где его может использовать пользователь.
Мне нужно сделать это синхронно, потому что мое приложение не может работать без базы данных.
Однако я не вижу способа сделать это синхронно. MS заставляет меня сделать это с помощью асинхронной задачи, например:
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;
}
Теперь я столкнулся с ситуацией, когда приложение запускалось до того, как база данных была скопирована в локальный каталог, поскольку копирование также должно выполняться асинхронно, просто нет способа сделать это синхронно. Нет такой функции, как StorageFile.Copy().
Поэтому я использую это:
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("");
}
}
Это сводит меня с ума.
Люди пишут, что Async следует обнимать, ценить и ценить, но в моем случае это не вызывает никаких проблем.
Я не вижу способа сделать эту вещь синхронной, и мне интересно, почему MS заставляет меня делать это таким образом.
Любая помощь очень ценится.
Спасибо.
Код в виде скриншота:
Изменить: я добавил лучшие методы здесь:
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;
}
}
3 ответа
Все, что вам нужно сделать, это:
//private async void pCopyFromResourcesToLocal(string uFileName) { ... }
private async Task pCopyFromResourcesToLocal(string uFileName) { ... }
и тогда вы можете ждать этого:
//pCopyFromResourcesToLocal(uFileName);
await pCopyFromResourcesToLocal(uFileName);
и все это будет завершено, прежде чем позвонить return (_dbApp = new DB(path));
Ничего в этом async
/await
цепь может произойти из строя.
Когда вы говорите, что ваше приложение не может работать без базы данных, помните, что использование ключевого слова await делает именно это, поэтому следующая строка кода не будет выполняться до тех пор, пока не вернется асинхронный вызов. Вы можете структурировать свой код так, чтобы он реагировал, пока вы ожидаете, когда БД вернется в оперативный режим.
Однако вы можете заставить свою функцию быть синхронной, используя такую конструкцию, как:
StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName)).GetAwaiter().GetResult();
Или, что еще лучше, взгляните на JoinableTaskFactory.
Любой асинхронный метод API можно сделать синхронным, просто пометив .GetAwaiter().GetResult()
в конце, как говорит @pm_2.
В случае Task<T>
результаты, вы можете просто использовать .Result
, и для Task
Результаты, .Wait()
,
Вы можете написать асинхронную функцию для чтения вашего файла, затем дождаться его результата на верхнем уровне, или вы можете написать синхронный метод и дождаться результата каждого вызова, который он выполняет для асинхронных функций.
Таким образом, Microsoft ни к чему вас не принуждает: она просто предоставляет более простой API с наименьшим общим знаменателем как асинхронных, так и синхронных рабочих процессов.