Реализация асинхронной службы WCF
У меня есть приложение WPF, которое я в настоящее время работаю над разделением на клиентскую и серверную стороны с помощью WCF. Мне не понравился беспорядок, который я изначально получил с прямым решением, поэтому теперь я перестраиваюсь, следуя рекомендациям в скринкасте Мигеля Кастро,WCF Extreme. Если вы не знакомы с видео он в основном устанавливает все связи вручную - без использования ссылок. Сюда входят:
- общий Контракт со всеми контрактами на обслуживание и данные-на которые ссылается клиент и сервер
- консольное приложение, размещающее службу
- прокси-классы на клиенте, отображающие службу и передающие ей вызовы (используя ClientBase или ClientFactory)
Я следил за всеми его шагами, и мне очень нравится, где это происходит. Однако он не обращается к асинхронным вызовам службы, и это то, что я хотел бы использовать.
при добавлении услуги Ссылка я могу проверить флажок "генерировать асинхронные операции", и я получаю MyServiceCompleted и MyServiceAsync. Однако, я предполагаю, что это то, что было создано при добавлении ссылки на службу, а не какая-то магия в классах за основу?
итак, могу ли я получить асинхронные операции от ClientBase или ClientFactory? Или я должен определить фактические услуги сервер асинхронный? Если да - может кто-нибудь дать мне несколько советов или примеров о том, как начать работу с простой асинхронный сервис? Я читал об этом на MSDN, но это оставило меня в замешательстве, чувствуя себя идиотом, потому что не получил этого уже..
3 ответов
реализация асинхронных операций на стороне сервера довольно проста. Убедитесь, что имена методов совпадают и имеют префиксы Begin и End. 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();
}
}
}
}
когда вы выбираете "генерировать асинхронные операции", как вы отметили, нет никакой магии: 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);
}
вы также можете явно запрограммировать свой сервис на работу асинхронно, как описано здесь:синхронные и асинхронные операции, С помощью [OperationContract(AsyncPattern=true)]
альтернативным способом достижения асинхронных операций на стороне клиента без использования svcutil является настройка интерфейса (ServiceContract) локально на стороне клиента, который реализует асинхронную операцию.
контракт на стороне сервера:
[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 вызывает проблемы, например, с повторным использованием классов.
ClientProxy
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();
}