none
Как вызывать веб службу без прокси? RRS feed

  • Общие обсуждения

  • В тех случаях, если вы не хотите использовать функцию “Add Service Reference”, вы должны понимать контракт предоставляемый службой, создавать локальные DataContracts и посылать сообщения вручную. Для этого вам нужно ссылаться на WSDL документ службы для определения способа создания локальных атрибутов DataContract и отправки сообщений.

    Допустим, вы имеете следующую службу:

    [ServiceContract]
         public interface IService1
         {
              [OperationContract]
              string DoWork(string name);
         }
    
    

    Как разработчик службы, вы знаете также о контракте. Но пользователи вашей службы имеют только WSDL документ. Часть этого документа выглядит, например, так:

    <wsdl:message name="IService1_DoWork_InputMessage">
     <wsdl:part name="parameters" element="tns:DoWork"/>
    </wsdl:message>
    
    <wsdl:message name="IService1_DoWork_OutputMessage">
     <wsdl:part name="parameters" element="tns:DoWorkResponse"/>
    </wsdl:message>
    
    <wsdl:operation name="DoWork">
     <wsdl:input wsaw:Action="http://tempuri.org/IService1/DoWork"   message="tns:IService1_DoWork_InputMessage"/>
     <wsdl:output wsaw:Action="http:"//tempuri.org/IService1/DoWorkResponse message="tns:IService1_DoWork_OutputMessage"/>
    </wsdl:operation>
    
    

    и xml схема:

    <xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org/">
     <xs:element name="DoWork">
      <xs:complexType>
       <xs:sequence>
        <xs:element minOccurs="0" name="name" nillable="true" type="xs:string"/>
       </xs:sequence>
      </xs:complexType>
     </xs:element>
     <xs:element name="DoWorkResponse">
      <xs:complexType>
       <xs:sequence>
        <xs:element minOccurs="0" name="DoWorkResult" nillable="true" type="xs:string"/>
       </xs:sequence>
      </xs:complexType>
     </xs:element>
    </xs:schema>
    
    Эта информация необходима для работы со службой без прокси.  Ниже представлены 2 способа работы со службой. Один из них заключается в создании канала клиента (что похоже на использование прокси), а другой – в передачи сообщений непосредственно.

    Первый способ:

     [ServiceContract]
      public interface IService1
      {
        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginDoWork(string name, AsyncCallback callback, object asyncState);
     
        string EndDoWork(System.IAsyncResult result);
      }
     
      public interface IService1Channel : IService1, IClientChannel { }
          ChannelFactory<IService1Channel> factory = new ChannelFactory<IService1Channel>(new BasicHttpBinding(), new EndpointAddress("http://localhost:60486/Service1.svc/"));
          factory.BeginOpen(openResult =>
          {
           factory.EndOpen(openResult);
           IService1Channel channel = factory.CreateChannel();
           channel.BeginOpen(channelOpenResult =>
           {
             channel.BeginDoWork("abc", new AsyncCallback((operationResult) =>
             {
               string result = channel.EndDoWork(operationResult);
               this.Dispatcher.BeginInvoke(new Action(() =>
               {
                 textBlock1.Text = result;
               }));
             }), null);
           }, null);
          }, null);
    
    
    • создайте контракт операции, основанный на WSDL документе, используя асинхронный паттерн. В WSDL выше у есть операция DoWork.
    • создайте контракт данных, основанный на WSDL документе. В WSDL выше, входные и выходные данные являются сложным типом, но так как они содержат только один элемент типа String, то мы можем использовать string для нашего контракта данных. Для более сложных схем придется создавать класс, соответствующий схеме.
    • создайте интерфейс канала, который наследует интерфейс IClientChannel и контракт операции, созданный выше.
    • создайте ChannelFactory с BasicHttpBinding и надлежащего EndpointAddress
    • откройте ChannelFactory и создайте канал
    • откройте канал и работайте с операцией
    • закройте канал и ChannelFactory (опущено в примере)

    Решение выше может выглядеть похоже на работу с сгенерированным прокси. Но он не содержит кода, такого как ***CompletedEventArgs. Так что, код получается немного чище, чем созданный прокси. Если вы хотите еще большего контроля над сообщениями между клиентом и службой, и если вы хотите полностью избавиться от контракта операции, можно создать IRequestChannel вместо канала клиента.

    BasicHttpBinding binding = new BasicHttpBinding();
    EndpointAddress address = new EndpointAddress("http://localhost:8731/Service1.svc");
    var factory = binding.BuildChannelFactory<IRequestChannel>();
    factory.BeginOpen(openResult => 
    {
         factory.EndOpen(openResult);
         IRequestChannel channel = factory.CreateChannel(address);
         channel.BeginOpen(channelOpenResult => 
         {
    Message requestmessage = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IService1/DoWork", new DoWork() { Name = "abc" });
              channel.BeginRequest(requestmessage, requestResult =>
              {
                  Message responseMessage = channel.EndRequest(requestResult);
                  DoWorkResponse response = responseMessage.GetBody<DoWorkResponse>();
                  string result = response.DoWorkResult;
    channel.BeginClose(channelCloseResult => 
                  {
                       channel.EndClose(channelCloseResult);
                       factory.BeginClose(closeResult => 
                       {
                            factory.EndClose(closeResult);
                       }, null);
                  }, null);
              }, null);
         }, null);
    }, null);
     
    [DataContract(Namespace = "http://tempuri.org/")]
    public class DoWork
    {
         [DataMember]
         public string Name { get; set; }
    }
     
    [DataContract(Namespace = "http://tempuri.org/")]
    public class DoWorkResponse
    {
         [DataMember]
         public string DoWorkResult { get; set; }
    }
    
    
    • создайте BasicHttpBinding
    • постройте ChannelFactory
    • откройте ChannelFactory и создайте IRequestChannel
    • откройте канал и отправьте сообщение
    • основываясь на xml схеме и WSDL создайте соответствующий класс DoWork. Убедитесь, что пространство имен является правильным. Экземпляр этого класса, используется в качестве текста сообщения.
    • после того, как получите ответное сообщение, создайте другой класс DoWorkResponse по описанию в WSDL.
    • закройте канал и ChannelFactory.

    Заметка: Хотя вы и можете открывать/закрывать фабрику и канал синхронно, вы должны посылать сообщения асинхронно. Чтобы избежать путаницы, вы можете делать все асинхронно.

     


    Для связи [mail]
    16 марта 2011 г. 13:30
    Модератор