Реализация службы async WCF
У меня есть приложение WPF, в котором я сейчас работаю над разделением на клиентскую и серверную стороны - с использованием WCF. Мне не нравился беспорядок, который я изначально получил с прямым решением, так что теперь я реструктурирую следующие рекомендации в скринкасте Мигеля Кастро, WCF Extreme. Если вы не знакомы с видео, он в основном настраивает все сообщение вручную - без использования ссылок на службы. Это включает в себя:
- Общий договор со всеми контрактами на обслуживание и передачу данных, на которые ссылаются клиент и сервер
- Консольное приложение, обслуживающее службу
- Прокси-классы на клиенте, сопоставляющие службу и передающие ему вызовы (с использованием ClientBase или ClientFactory)
Я выполнил все его шаги, и мне очень нравится, где это происходит. Однако он не обращается к асинхронным вызовам службы, и это то, что я хотел бы использовать.
При добавлении ссылки на службу я могу установить флажок "Сгенерировать асинхронные операции", и я получаю MyServiceCompleted и MyServiceAsync. Тем не менее, я думаю, это то, что было создано при добавлении ссылки на службу, а не какая-то магия в классы, на которых это происходит?
Итак, могу ли я как-то получить асинхронные операции из ClientBase или ClientFactory? Или мне нужно определить, какие службы на стороне сервера будут асинхронными? Если да - может кто-нибудь, пожалуйста, дайте мне несколько советов или примеров о том, как начать работу с простой службой асинхронного обслуживания? Я читал в MSDN по этому вопросу, но это оставило меня все смущенное чувство, как идиот, чтобы не получить это уже.
Ответы
Ответ 1
Реализация асинхронных операций на стороне сервера довольно проста. Убедитесь, что имена методов совпадают, и они начинаются с начала и конца. GetImageAsyncResult - это обычная реализация IAsyncResult (множество примеров в Интернете).
public class MapProvider : IMapProvider //implementation - belongs to server
{
public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
{
GetImageAsyncResult asyncResult = new GetImageAsyncResult(level, x, y, layers, callback, state);
ThreadPool.QueueUserWorkItem(Callback, asyncResult);
return asyncResult;
}
private void Callback(object state)
{
GetImageAsyncResult asyncResult = state as GetImageAsyncResult;
asyncResult.Image = TileProvider.GetImage(asyncResult.Level, asyncResult.X, asyncResult.Y, asyncResult.Layers);
asyncResult.Complete();
}
public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
{
using (GetImageAsyncResult asyncResult = result as GetImageAsyncResult)
{
asyncResult.AsyncWait.WaitOne();
return asyncResult.Image;
}
}
}
public class MapProviderProxy : ClientBase<IMapProvider>, IMapProvider, IDisposable
{
public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
{
return Channel.BeginGetImage(level, x, y, layers, callback, state);
}
public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
{
return Channel.EndGetImage(result);
}
public void Dispose()
{
if (State == CommunicationState.Faulted)
{
Abort();
}
else
{
try
{
Close();
}
catch
{
Abort();
}
}
}
}
Ответ 2
Когда вы выбираете "Сгенерировать асинхронные операции", как вы отметили, магия не задействована: Begin...
запустит ваше общение, сервер начнет обрабатывать материал, но вы не сможете ничего использовать, пока не назовете End...
.
Все это происходит на стороне клиента, поэтому вам не нужно ничего менять при реализации своей службы.
Вероятно, вы думаете, что это должно быть сложным, но это не так;)
EDIT: Приведем пример:
using (Service.SampleClient client = new Service.SampleClient())
{
client.AddCompleted +=
(object sender, Service.AddCompletedEventArgs e)
{
Console.WriteLine(e.Result); // 2
};
client.AddAsync(1, 1);
// wait for async callback
Console.ReadLine();
}
[ServiceContract]
public interface ISample
{
[OperationContract]
int Add(int a, int b);
}
Вы также можете явно запрограммировать свою службу для работы async, как описано здесь: Синхронные и асинхронные операции, используя [OperationContract(AsyncPattern=true)]
Ответ 3
Альтернативный способ выполнения асинхронных операций на стороне клиента без использования svcutil - это настроить интерфейс (ServiceContract) локально на стороне клиента, который реализует операцию async.
Контракт на стороне сервера:
[ServiceContract]
public interface IServerContract
{
[OperationContract]
string GetData(int value);
}
Асинхронный контракт на стороне клиента
[ServiceContract(Namespace = "http://tempuri.org/", Name = "IServerContract")]
public interface IClientContractAsync
{
[OperationContract]
Task<string> GetDataAsync(int value);
}
Обратите внимание, что имя и пространство имен по умолчанию должны быть установлены в клиентском контракте, чтобы соответствовать пространству имен контрактных серверов. Итак, теперь у вас есть асинхронная операция (без новых новых потоков). Таким образом, вам не нужно выполнять какую-либо конкретную реализацию на стороне сервера. Конечно, это похоже на то, что делает SvcUtil, но SvcUtil генерирует много дополнительного кода, и иногда я нахожу svcutil, вызывающий проблемы, например. с повторным использованием классов.
Клиентский прокси
public class ClientProxy : ClientBase<IClientContractAsync>
{
public IClientContractAsync ChannelOut
{
get
{
return Channel;
}
}
}
Использование клиента:
static void Main(string[] args)
{
var client = new ClientProxy();
client.Open();
var asyncReadValue = client.ChannelOut.GetDataAsync(45).Result;
Console.WriteLine(asyncReadValue);
Console.ReadLine();
}
Класс сервера
public class ServerService : IServerContract
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Хост сервера
static void Main(string[] args)
{
var server = new ServiceHost(typeof(ServerService));
server.Open();
Console.WriteLine("started");
Console.ReadLine();
}