Если говорить прямо о фундаментальных принципах, то, что вы на самом деле запрашиваете, - это возможность сделать синхронный вызов в API, который выполняет асинхронную задачу, но возвращается только после завершения этой асинхронной задачи.Чтобы сформулировать это по-другому, вы хотите объединить фазы Begin и End асинхронной операции обратно в одну атомарную синхронную операцию.
Единственный способ добиться этого - заблокировать поток, выполняющий вызов, до тех пор, показавершена фаза асинхронной операции.По ряду причин это не очень хорошая идея.
Если вы серьезно относитесь к использованию Silverlight, вам придется проглотить его асинхронный характер и работать в этой среде, а не пытаться принудительно вернуть его обратно в синхронную систему.
Преобразование синхронного в асинхронное
Прочитайте этот блог о преобразовании синхронного кода в асинхронный код.Прочитав это, теперь давайте просто представим, что ваш код использует синхронный метод в вашей новой DLL с именем DownloadYourDataObjects
, который имеет такую воображаемую сигнатуру:REST базовый сервис и преобразует его в набор YourDataObject
экземпляров.Мнимый синхронный код для отображения этого набора данных может быть: -
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
try
{
LoadMyData();
}
catch (Exception err)
{
// Oops something bad happened show err.
}
}
private void LoadMyData()
{
DataItemsListBox.ItemsSource = DownloadYourDataObjects(someUri);
}
Поскольку Silverlight WebClient
является асинхронным, нам необходимо преобразовать всю эту цепочку кода для работы в асинхронном режиме.
Используя AsyncOperationService
из блога, нам сначала нужно преобразовать DownloadYourDataObjects
, чтобы вместо него вернуть AsyncOperation
.Он будет иметь такую подпись (см. Ниже идею реализации): -
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult);
Код использования будет выглядеть примерно так: -
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
LoadMyData().Run(err =>
{
if (err != null)
{
// Oops something bad happened show err.
}
});
}
private IEnumerable<AsyncOperation> LoadMyData()
{
yield return DownloadYourDataObjects(someUri, result =>
{
DataItemsListBox.ItemsSource = result;
});
}
Это может выглядетьЛитт OTT, но на самом деле это не намного больше кода, чем оригинальная "синхронная" версия.В этом простом случае LoadMyData
выполнялась только одна операция.Более сложная версия LoadMyData
может содержать несколько других операций, которые также должны быть асинхронными.В таком случае эти операции будут просто другими yield
точками в коде, базовая логическая структура LoadMyData
не сильно изменится по сравнению с исходной синхронной версией.
Вот пример реализацииDownloadYourDataObjects
, которую поставит ваша DLL.
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult)
{
return (completed) =>
{
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, args) =>
{
try
{
returnResult(ConvertStringToYourDataObjects(args.Result));
completed(null);
}
catch (Exception err)
{
completed(err);
}
};
client.DownloadStringAsync(source);
};
}