Async CTP - Как я могу использовать async/await для вызова службы wcf?
Если я вызову метод службы WCF, я бы сделал что-то вроде этого:
proxy.DoSomethingAsync();
proxy.DoSomethingAsyncCompleted += OnDoSomethingAsyncCompleted;
Как я могу сделать то же самое с помощью нового async
ctp?
Наверное, мне нужно что-то вроде proxy.DoSomethingTaskAsync
или proxy.DoSomethingAsync().ToTask()
? Вызов веб-службы должен вернуть Task<T>
, чтобы использовать ключевое слово await
, но как
Ответы
Ответ 1
В CTP существуют методы factory, которые выполняют работу по превращению регулярных APM-функций (Begin/End) в те, которые совместимы с новым ключевым словом async, например:
Stream s = new FileStream("C:\test.txt", FileMode.CreateNew);
byte []buffer = new byte[100];
int numBytesRead = await Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);
Итак, в вашем случае вы можете сделать эквивалент, а затем вы будете называть его так:
async proxy.DoSomethingTaskAsync()
Смотрите этот поток в группе обсуждения CTP для получения дополнительной информации
Ответ 2
Асинхронный сервис с использованием async-ожидания очень чувствителен, поскольку он может чередовать многие вызовы клиентов и выполнять их параллельно (2).
Несмотря на это, служба может работать полностью поточно-защищенной в одном потоке (3) и может быть однопользовательской службой (1) или объектом службы, созданной инфраструктурой для сеанса или только для вызова.
При внедрении службы обратите внимание на ServiceBehaviourAttributes (1)... (3):
[ServiceContract( Namespace="X", Name="TheContract" )]
public interface IAsyncContractForClientAndService
{
[OperationContract]
Task<TResponse> SendReceiveAsync( TRequest req );
}
[ServiceBehavior (InstanceContextMode = InstanceContextMode.Single, // (1)
// also works with InstanceContextMode.PerSession or PerCall
ConcurrencyMode = ConcurrencyMode.Multiple, // (2)
UseSynchronizationContext = true)] // (3)
public MyService : IAsyncContractForClientAndService
{
public async Task<TResponse> SendReceiveAsync( TRequest req )
{
DoSomethingSynchronous();
await SomethingAsynchronous();
// await lets other clients call the service here or at any await in
// subfunctions. Calls from clients execute 'interleaved'.
return new TResponse( ... );
}
}
Чтобы запускать каждый вызов в одном потоке, в момент открытия() ServiceHost должен присутствовать System.Threading.SynchronizationContext.Current!= null.
Используя SynchronizationContext, вам не нужно заботиться о блокировках. Атомные, не прерываемые разделы кода растягиваются примерно от одного ожидания до следующего.
Позаботьтесь о том, чтобы общие данные службы находились в согласованном состоянии при каждом ожидании, и имейте в виду, что последовательные запросы от одного клиента могут отвечать не в том порядке, в котором они были отправлены.
С клиентской стороны ожидание работы асинхронной службы:
var response = await client.Channel.SendReceiveAsync( request );
Невозможно использовать или отредактировать параметры в контракте операции. Все данные ответа должны быть переданы возвращаемым значением Task (T).
Я использую этот интерфейс в AsyncWcfLib, он поддерживает модель программирования на основе актера.
Ответ 3
В Async CTP есть образец WCF, который покажет вам, как использовать модель async/wait в WCF.
С точки зрения планов поддержки этой модели в WCF вы можете взглянуть на следующее сообщение:
http://blogs.msdn.com/b/endpoint/archive/2010/11/13/simplified-asynchronous-programming-model-in-wcf-with-async-await.aspx
Надеюсь, что это поможет.
Амадео
Ответ 4
Как упоминалось Мэттом, существует метод TaskFactory.FromAsync
, который позволяет вам создать Task
из пары Begin
/End
. Вам нужно включить асинхронные конечные точки, когда вы добавите ссылку на WCF, а затем вы можете их обернуть, используя методы расширения.
Как упоминалось Amadeo, в этом примере есть пример этого в Async CTP, в каталоге (C# WCF) Stock Quotes
.
Также в этом каталоге есть проект TaskWsdlImportExtension
. Добавьте ссылку на эту DLL и измените свой .config как таковой:
<configuration>
<system.serviceModel>
<client>
<metadata>
<wsdlImporters>
<extension type="TaskWsdlImportExtension.TaskAsyncWsdlImportExtension, TaskWsdlImportExtension" />
</wsdlImporters>
</metadata>
</client>
</system.serviceModel>
</configuration>
Тогда вам не нужно делать свою собственную упаковку вообще; TaskWsdlImportExtension
сделает это за вас.
Ответ 5
Чаще всего есть асинхронные клиенты, вызывающие синхронный сервис.
Следующие клиентские и служебные контракты соответствуют (бит-магия используется за кулисами):
[ServiceContract( Namespace="X", Name="TheContract" )]
public interface IClientContractAsynchronous
{
[OperationContract]
Task<TResponse> SendReceiveAsync( TRequest req );
}
[ServiceContract( Namespace="X", Name="TheContract" )]
public interface IServiceContractSynchronous
{
[OperationContract]
TResponse SendReceive( TRequest req );
}
Клиентский интерфейс прямо ждет:
var response = await client.Channel.SendReceiveAsync( request );
Невозможно использовать или отредактировать параметры в операционном контракте. Все данные ответа должны быть переданы в возвращаемом значении. На самом деле это было для меня изменением.
Я использую этот интерфейс в AsyncWcfLib, он поддерживает модель программирования на основе актера.
Ответ 6
Вы справедливо отметили, что async/await построены вокруг методов, возвращающих Task and Task (для отличного объяснения работы async/await, см. здесь). Здесь, как создавать методы на основе задачи для службы WCF:
-
В Visual Studio 2012 RC есть дополнительный флажок в диалоговом окне "Настройка служебной ссылки": "Генерировать операции на основе задач". Хотя я не вижу этот параметр, зарегистрированный в MSDN, я ожидаю, что он существует специально для разрешения асинхронного/ожидающего вызовов WCF.
-
Для более ранних версий ознакомьтесь с этим сообщением, описывающим расширение, которое позволяет создавать методы на основе Task < > на WCF даже с CTP.