Уж сколько раз хоронили 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, неужели проблема настолько неактуальна? Поделитесь мнением в комменариях! :)