Уж сколько раз хоронили Silverlight, а в некоторых, особенно, внутрикорпоративных проектах он всё еще живет и здравствует, и даже активно развивается.
В один из подобных проектов и ступила недавно нога человека (моя, то есть :)).
Типичной задачей в Silverlight апплетах является, конечно, взаимодействие с сервером.
Использование технологии RIA Services (aka Domain Services), некоторое время активно пиарившейся Майкрософтом, лично у меня оставило весьма двойственные впечатления — в первую очередь из-за неочевидности происходящего «под капотом» и ощущения некоей магии происходящего. Поэтому старые добрые WCF-сервисы для взаимодействия с Сильверлайтом остаются для меня приоритетом.
С моего последнего появления в Сильверлайте прошло достаточно много времени, и я надеялся, что взаимодействие с WCF там значительно улучшилось. Оказалось же, что все в общем-то по-прежнему, и чтобы вызвать какой-нибудь метод сервиса вроде
[ServiceContract]
public interface ITestService
{
[OperationContract]
int Operation(int value);
}
Нужно написать что-то вроде:
var client = new Service2Client();
EventHandler<TestMethodReturningDataCompletedEventArgs> handler = null;
handler = (s, args) =>
{
client.TestMethodReturningDataCompleted -= handler;
Dispatcher.BeginInvoke(() =>
{
//some UI method here
MessageBox.Show(args.Result.ToString());
});
};
client.TestMethodReturningDataCompleted += handler;
client.TestMethodReturningDataAsync();
Некоторая непоследовательность вызовов и общая непонятность кода налицо. И это при наличии Task Parallel Library, которая уже стала стандартом де-факто при работе с асинхронностью в .net! Отказавшись мириться с безобразием, происходящим в сильверлайт, я нашел замечательное решение: SyncWCF.
Выносим интерфейс сервиса в Portable Class Library, подключаем его к SL проекту и теперь вызов серсвиса становится куда более приятным!
var channel = new AsyncChannelFactory<IService2>(new BasicHttpBinding() { }, null)
.CreateChannel(new EndpointAddress("/Service2.svc"));
var result = await channel.ExecuteAsync(ws => ws.TestMethodReturningData());
MessageBox.Show(result.ToString());
К слову, для подобного использования пришлось чуть-чуть дописать библиотеку SyncWCF, потому что в оригинале она настроена на работу с callback’ами.
А если к этому добавить еще пару атрибутов, для нормальной передачи исключений в Silverlight (по умолчанию исключения Сильверлайт, конечно, перехватывает, только текст ошибок в них всех одинаков — «Not Found» :)), то получается совсем привычный «псевдо-синхронный» код.
try
{
await channel.ExecuteAsync(ws => ws.TestMethodThrowingException());
}
catch (Exception ex)
{
//some error-handling logic is meant to be here
MessageBox.Show(ex.ToString());
}
Рабочий пример с исходными кодами — по ссылке. Там же и слегка измененные исходники SyncWCF (до принятия диффа в основной репозиторий)
Вообще, я откровенно удивлен небольшой популярности проекта SyncWCF, неужели проблема настолько неактуальна? Поделитесь мнением в комменариях! :)