Реализация асинхронной службы 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();
}