<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Windows Communication Foundation入門(Part Three)

      《Windows Communication Foundation之旅》系列之三

      示例代碼下載:DuplexSample.rar

      四、Service Contract編程模型
      在Part Two中,我以“Hello World”為例講解了如何定義一個Service。其核心就是為接口或類施加ServiceContractAttribute,為方法施加OperationContractAttribute。在Service的方法中,可以接受多個參數,也可以有返回類型,只要這些數據類型能夠被序列化。這樣一種方式通常被稱為本地對象,遠程過程調用(local-object, Remoting-Procedure-Call)方式,它非常利于開發人員快速地進行Service的開發。

      在Service Contract編程模型中,還有一種方式是基于Message Contract的。服務的方法最多只能有一個參數,以及一個返回值,且它們的數據類型是通過Message Contract自定義的消息類型。在自定義消息中,可以為消息定義詳細的Header和Body,使得對消息的交換更加靈活,也更利于對消息的控制。

      一個有趣的話題是當我們定義一個Service時,如果一個private方法被施加了OperationContractAttribute,那么對于客戶端而言,這個方法是可以被調用的。這似乎與private對于對象封裝的意義有矛盾。但是這樣的規定是有其現實意義的,因為對于一個服務而言,服務端和客戶端的需求往往會不一致。在服務端,該服務對象即使允許被遠程調用,但本地調用卻可能會因情況而異。如下面的服務定義:
      [ServiceContract]
      public class BookTicket
      {
       [OperationContract]
       public bool Check(Ticket ticket)
       {
        bool flag;
        //logic to check whether the ticket is none;
        return flag;
       }
       [OperationContract]
       private bool Book(Ticket ticket)
       {
        //logic to book the ticket
       }
      }
      在服務類BookTicket中,方法Check和Book都是服務方法,但后者被定義成為private方法。為什么呢?因為對于客戶而言,首先會檢查是否還有電影票,然而再預定該電影票。也就是說這兩項功能都是面向客戶的服務,會被遠程調用。對于Check方法,除了遠程客戶會調用該方法之外,還有可能被查詢電影票、預定電影票、出售電影票等業務邏輯所調用。而Book方法,則只針對遠程客戶,只可能被遠程調用。為了保證該方法的安全,將其設置為private,使得本地對象不至于調用它。

      因此在WCF中,一個方法是否應該被設置為服務方法,以及應該設置為public還是private,都需要根據具體的業務邏輯來判斷。如果涉及到私有的服務方法較多,一種好的方法是利用設計模式的Fa?ade模式,將這些方法組合起來。而這些方法的真實邏輯,可能會散放到各自的本地對象中,對于這些本地對象,也可以給與一定的訪問限制,如下面的代碼所示:
      internal class BusinessObjA
      {
       internal void FooA(){}
      }
      internal class BusinessObjB
      {
       internal void FooB(){}
      }
      internal class BusinessObjC
      {
       internal void FooC(){}
      }
      [ServiceContract]
      internal class Fa?ade
      {
       private BusinessObjA objA = new BusinessObjA();
       private BusinessObjB objB = new BusinessObjB();
       private BusinessObjC objC = new BusinessObjC();
       [OperationContract]
       private void SvcA()
       {
        objA.FooA();
       }
       [OperationContract]
       private void SvcB()
       {
        objB.FooB();
       }
       [OperationContract]
       private void SvcC()
       {
        objC.FooC();
       }
      }
      方法FooA,FooB,FooC作為internal方法,拒絕被程序集外的本地對象調用,但SvcA,SvcB和SvcC方法,卻可以被遠程對象所調用。我們甚至可以將BusinessObjA,BusinessObjB等類定義為Fa?ade類的嵌套類。采用這樣的方法,有利于這些特殊的服務方法,被遠程客戶更方便的調用。

      定義一個Service,最常見的還是顯式地將接口定義為Service。這樣的方式使得服務的定義更加靈活,這一點,我已在Part Two中有過描述。當然,采用這種方式,就不存在前面所述的私有方法成為服務方法的形式了,因為在一個接口定義中,所有方法都是public的。

      另外一個話題是有關“服務接口的繼承”。一個被標記了[ServiceContract]的接口,在其繼承鏈上,允許具有多個同樣標記了[ServiceContract]的接口。對接口內定義的OperationContract方法,則是根據“聚合”的原則,如下的代碼所示:
      [ServiceContract]
      public interface IOne
      {
          [OperationContract(IsOneWay=true)]
          void A();
      }
      [ServiceContract]
      public interface ITwo
      {
          [OperationContract]
          void B();
      }
      [ServiceContract]
      public interface IOneTwo : IOne, ITwo
      {
          [OperationContract]
          void C();
      }

      在這個例子中,接口IOneTwo繼承了接口IOne和ITwo。此時服務IOneTwo暴露的服務方法應該為方法A、B和C。

      然而當我們采用Duplex消息交換模式(文章后面會詳細介紹Duplex)時,對于服務接口的回調接口在接口繼承上有一定的限制。WCF要求服務接口IB在繼承另一個服務接口IA時,IB的回調接口IBCallBack必須同時繼承IACallBack,否則會拋出InvalidContractException異常。正確的定義如下所示:
      [ServiceContract(CallbackContract = IACallback)]
      interface IA {}
      interface IACallback {}

      [ServiceContract(CallbackContract = IBCallback)]
      interface IB : IA {}
      interface IBCallback : IACallback {}

      五、消息交換模式(Message Exchange Patterns,MEPS)
      在WCF中,服務端與客戶端之間消息的交換共有三種模式:Request/Reply,One-Way,Duplex。

      1、Request/Reply
      這是默認的一種消息交換模式,客戶端調用服務方法發出請求(Request),服務端收到請求后,進行相應的操作,然后返回一個結果值(Reply)。

      如果沒有其它特別的設置,一個方法如果標記了OperationContract,則該方法的消息交換模式就是采用的Request/Reply方式,即使它的返回值是void。當然,我們也可以將IsOneWay設置為false,這也是默認的設置。如下的代碼所示:
      [ServiceContract]
      public interface ICalculator
      {
       [OperationContract]
       int Add(int a, int b);

       [OperationContract]
       int Subtract(int a, int b);
      }

      2、One-Way
      如果消息交換模式為One-Way,則表明客戶端與服務端之間只有請求,沒有響應。即使響應信息被發出,該響應信息也會被忽略。這種方式類似于消息的通知或者廣播。當一個服務方法被設置為One-Way時,如果該方法有返回值,會拋出InvalidOperationException異常。

      要將服務方法設置為One-Way非常簡單,只需要將OperationContractAttribute的屬性IsOneWay設置為true就可以了,如下的代碼所示:
      public class Radio
      {
       [OperationContract(IsOneWay=true)]
       private void BroadCast();
      }

      3、Duplex
      Duplex消息交換模式具有客戶端與服務端雙向通信的功能,同時它的實現還可以使消息交換具有異步回調的作用。

      要實現消息交換的Duplex,相對比較復雜。它需要定義兩個接口,其中服務接口用于客戶端向服務端發送消息,而回調接口則是從服務端返回消息給客戶端,它是通過回調的方式來完成的。接口定義如下:
      服務接口:
      [ServiceContract(Namespace = "http://microsoft.servicemodel.samples/",
      Session = true, CallbackContract=typeof(ICalculatorDuplexCallback))]
      public interface ICalculatorDuplex
      {
          [OperationContract(IsOneWay=true)]
          void Clear();

          [OperationContract(IsOneWay = true)]
          void AddTo(double n);

          [OperationContract(IsOneWay = true)]
          void SubtractFrom(double n);

          [OperationContract(IsOneWay = true)]
          void MultiplyBy(double n);

          [OperationContract(IsOneWay = true)]
          void DivideBy(double n);
      }
      回調接口:
      public interface ICalculatorDuplexCallback
      {
          [OperationContract(IsOneWay = true)]
          void Equals(double result);

          [OperationContract(IsOneWay = true)]
          void Equation(string equation);
      }
      注意在接口定義中,每個服務方法的消息轉換模式均設置為One-Way。此外,回調接口是被本地調用,因此不需要定義[ServiceContract]。在服務接口中,需要設置ServiceContractAttribute的CallbackContract屬性,使其指向回調接口的類型type。

      對于實現服務的類,實例化模式(InstanceContextMode)究竟是采用PerSession方式,還是PerCall方式,應根據該服務對象是否需要保存狀態來決定。如果是PerSession,則服務對象的生命周期是存活于一個會話期間。而PerCall方式下,服務對象是在方法被調用時創建,結束后即被銷毀。然而在Duplex模式下,不能使用Single方式,否則會導致異常拋出。本例的實現如下:
      [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
      public class CalculatorService : ICalculatorDuplex
      {
          double result;
          string equation;
          ICalculatorDuplexCallback callback = null;
          public CalculatorService()
          {
              result = 0.0D;
              equation = result.ToString();
              callback = OperationContext.Current.
              GetCallbackChannel();
          }
          public void AddTo(double n)
          {
              result += n;
              equation += " + " + n.ToString();
              callback.Equals(result);
          }
         // Other code not shown.
      }

      在類CalculatorService中,回調接口對象callback通過OperationContext.Current.GetCallbackChannel<>()獲取。然后在服務方法例如AddTo()中,通過調用該回調對象的方法,完成服務端向客戶端返回消息的功能。

      在使用Duplex時,Contract使用的Binding應該是系統提供的WSDualHttpBinding,如果使用BasicHttpBinding,會出現錯誤。因此Host程序應該如下所示:
          public static void Main(string[] args)
          {
              Uri uri = new Uri("http://localhost:8080/servicemodelsamples");
              using (ServiceHost host = new ServiceHost(typeof(CalculatorService), uri))
              {
                  host.AddServiceEndpoint(typeof(ICalculatorDuplex),new WSDualHttpBinding(),"service.svc");
                  host.Open();
                  Console.WriteLine("Press any key to quit service.");
                  Console.ReadKey();
              }
          }
      如果是使用配置文件,也應作相應的修改,如本例:
        <system.serviceModel>
          <client>
            <endpoint name=""
                      address="http://localhost:8080/servicemodelsamples/service.svc"
                      binding="wsDualHttpBinding"
                      bindingConfiguration="DuplexBinding"
                      contract="ICalculatorDuplex" />
          </client>
          <bindings>
            <!-- configure a binding that support duplex communication -->
            <wsDualHttpBinding>
              <binding name="DuplexBinding"
                       clientBaseAddress="http://localhost:8000/myClient/">
              </binding>
            </wsDualHttpBinding>
          </bindings>
        </system.serviceModel>

      當服務端將信息回送到客戶端后,對消息的處理是由回調對象來處理的,所以回調對象的實現應該是在客戶端完成,如下所示的代碼應該是在客戶端中:
          public class CallbackHandler : ICalculatorDuplexCallback
          {
              public void Equals(double result)
              {
                  Console.WriteLine("Equals({0})", result);
              }
              public void Equation(string equation)
              {
                  Console.WriteLine("Equation({0})", equation);
              }
          }

      客戶端調用服務對象相應的為:
          class Client
          {
              static void Main()
              {
                  // Construct InstanceContext to handle messages on
                  // callback interface.
                  InstanceContext site = new InstanceContext(new CallbackHandler());

                  // Create a proxy with given client endpoint configuration.
                  using (CalculatorDuplexProxy proxy =
                  new CalculatorDuplexProxy(site, "default"))
                  {
                      double value = 100.00D;
                      proxy.AddTo(value);
                      value = 50.00D;
                      proxy.SubtractFrom(value);
                      // Other code not shown.

                      // Wait for callback messages to complete before
                      // closing.
                      System.Threading.Thread.Sleep(500);
                      // Close the proxy.
                      proxy.Close();
                  }
              }
          }

      注意在Duplex中,會話創建的時機并不是客戶端創建Proxy實例的時候,而是當服務對象的方法被第一次調用時,會話方才建立,此時服務對象會在方法調用之前被實例化,直至會話結束,服務對象都是存在的。

      以上的代碼例子在WinFX的SDK Sample中可以找到。不過該例子并不能直接反映出Duplex功能。通過前面的介紹,我們知道Duplex具有客戶端與服務端雙向通信的功能,同時它的實現還可以使消息交換具有異步回調的作用。因此,我分別實現了兩個實例來展現Duplex在兩方面的作用。

      (1)客戶端與服務端雙向通信功能——ChatDuplexWin
      實例說明:一個類似于聊天室的小程序。利用Duplex支持客戶端與服務端通信的特點,實現了客戶端與服務端聊天的功能。

      服務接口和回調接口的定義如下:
          [ServiceContract(Namespace = "http://www.brucezhang.com/WCF/Samples/ChatDuplex", Session = true, CallbackContract=typeof(IChatDuplexCallback))]
          public interface IChatDuplex
          {
              [OperationContract(IsOneWay=true)]
              void Request(string cltMsg);
              [OperationContract(IsOneWay = true)]
              void Start();
          }
          public interface IChatDuplexCallback
          {
              [OperationContract(IsOneWay=true)]
              void Reply(string srvMsg);
          }
      很明顯,Request方法的功能為客戶端向服務端發送消息,Reply方法則使服務端回送消息給客戶端。服務接口IChatDuplex中的Start()方法,用于顯示的建立一個會話,因為在這個方法中,我需要直接獲取callback對象,使得服務端不必等待客戶端先發送消息,而是可以利用callback對象主動先向客戶端發送消息,從而實現聊天功能。

      實現類的代碼如下:
          [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
          public class ChatDuplex:IChatDuplex
          {
              public ChatDuplex()
              {
                  m_callback = OperationContext.Current.GetCallbackChannel();           
              }
              private IChatDuplexCallback m_callback = null;           
              public void Request(string cltMsg)
              {
                  ChatRoomUtil.MainForm.FillListBox(string.Format("Client:{0}", cltMsg));           
              }
              public void Start()
              {
                  ChatRoomUtil.MainForm.SetIIMDuplexCallback(m_callback);
              }
          }

      因為我要求在服務端界面中,能夠將客戶端發送來的消息顯示在主窗體界面中。所以利用了全局變量MainForm,用來保存主窗體對象:
          public static class ChatRoomUtil
          {
              public static ServerForm MainForm = new ServerForm();
          }
      而在服務端程序運行時,Application運行的主窗口也為該全局變量:
      Application.Run(ChatRoomUtil.MainForm);

      要實現聊天功能,最大的障礙是當服務端收到客戶端消息時,不能立即Reply消息,而應等待服務端用戶輸入回送的消息內容,方可以Reply。也即是說,當客戶端調用服務對象的Request方法時,不能直接調用callback對象。因此我利用Start()方法,將服務對象中獲得的callback對象傳遞到主窗體對象中。這樣,callback對象就可以留待服務端發送消息時調用了:
          public partial class ServerForm : Form
          {       
              private IChatDuplexCallback m_callback;
              private void btnSend_Click(object sender, EventArgs e)
              {
                  if (txtMessage.Text != string.Empty)
                  {
                      lbMessage.Items.Add(string.Format("Server:{0}", txtMessage.Text));
                      if (m_callback != null)
                      {
                          m_callback.Reply(txtMessage.Text);
                      }
                      txtMessage.Text = string.Empty;
                  }
              }       
              public void FillListBox(string message)
              {
                  lbMessage.Items.Add(message);
              }
              public void SetIIMDuplexCallback(IChatDuplexCallback callback)
              {
                  m_callback = callback;
              }
           //Other code not shown;
          }

      對于客戶端的實現,相對簡單,需要注意的是回調接口的實現:
          public class ChatDuplexCallbackHandler:IChatDuplexCallback
          {
              public ChatDuplexCallbackHandler(ListBox listBox)
              {
                  m_listBox = listBox;
              }
              private ListBox m_listBox;       

              public void Reply(string srvMsg)
              {
                  m_listBox.Items.Add(string.Format("Server:{0}", srvMsg));
              }
          }
      由于我自定義了該對象的構造函數,所以在實利化proxy時會有稍微區別:
      InstanceContext site = new InstanceContext(new ChatDuplexCallbackHandler(this.lbMessage));
      proxy = new ChatDuplexProxy(site);
      proxy.Start();

      通過proxy對象的Start()方法,使得我們在建立proxy對象開始,就創建了會話,從而使得服務對象被實例化,從而得以運行下面的這行代碼:
      m_callback = OperationContext.Current.GetCallbackChannel(); 

      也就是說,在proxy對象建立之后,服務端就已經獲得callback對象了,這樣就可以保證服務端能夠先向客戶端發送消息而不會因為callback為null,導致錯誤的發生。

      (2)消息交換的異步回調功能——AsyncDuplexWin
      實例說明:本實例比較簡單,只是為了驗證當回調對象被調用時,客戶端是否可以被異步運行。調用服務對象時,服務端會進行一個累加運算。在運算未完成之前,客戶端會執行顯示累加數字的任務,當服務端運算結束后,只要客戶端程序的線程處于Sleep狀態,該回調對象就會被調用,然后根據用戶選擇是否再繼續運行剩下的任務。本例中服務端為控制臺應用程序,客戶端則為Windows應用程序。

      例子中的接口定義非常簡單,不再贅述,而實現類的代碼如下:
          public class SumDuplex:ISumDuplex
          {
              public SumDuplex()
              {
                  callback = OperationContext.Current.GetCallbackChannel();
              }
              private ISumDuplexCallback callback = null;

              #region ISumDuplex Members
              public void Sum(int seed)
              {
                  int result = 0;
                  for (int i = 1; i < = seed; i++)
                  {
                      Thread.Sleep(10);
                      Console.WriteLine("now at {0}",i);
                      result += i;               
                  }
                  callback.Equals(result);
              }
              #endregion
          }
      很顯然,當客戶端調用該服務對象時,會在服務端的控制臺上打印出迭代值。

      由于客戶端需要在callback調用時,停止對當前任務的運行,所以需要用到多線程機制:
          public delegate void DoWorkDelegate();
          public partial class ClientForm : Form
          {
              public ClientForm()
              {
                  InitializeComponent();
                  InstanceContext site = new InstanceContext(new SumDuplexCallbackHandler(this.lbMessage));
                  proxy = new SumDuplexProxy(site);           
              }
              private SumDuplexProxy proxy;
              private Thread thread = null;
              private DoWorkDelegate del = null;       
              private int counter = 0;

              private void btnStart_Click(object sender, EventArgs e)
              {
                  proxy.Sum(100);
                  thread = new Thread(new ThreadStart(delegate()
                  {
                      while (true)
                      {
                          if (ClientUtil.IsCompleted)
                          {
                              if (MessageBox.Show("Game over,Exit?", "Notify", MessageBoxButtons.YesNo,
                                  MessageBoxIcon.Question) == DialogResult.Yes)
                              {
                                  break;
                              }
                          }
            if (counter > 10000)
                          {
                              break;
                          }
                          if (del != null)
                          {
                              del();
                          }
                          Thread.Sleep(50);
                      }
                  }
                  ));
                  del += new DoWorkDelegate(DoWork);
                  thread.Start();
              }                  

              private void DoWork()
              {
                  if (lbMessage.InvokeRequired)
                  {
                      this.Invoke(new DoWorkDelegate(DoWork));
                  }
                  else
                  {
                      lbMessage.Items.Add(counter);               
                      lbMessage.Refresh();
                      counter++;
                  }
              }
              private void ClientForm_FormClosing(object sender, FormClosingEventArgs e)
              {
                  if (thread != null)
                  {
                      thread.Abort();
                  }
              }
          }

      因為需要在多線程中對ListBox控件的items進行修改,由于該控件不是線程安全的,所以應使用該控件的InvokeRequired屬性。此外,在線程啟動時的匿名方法中,利用while(true)控制當前線程的運行,并利用全局變量ClientUtil.IsCompleted判斷回調對象是否被調用,如果被調用了,則會彈出對話框,選擇是否退出當前任務。這里所謂的當前任務實際上就是調用DoWork方法,向ListBox控件的items中不斷添加累加的counter值。注意客戶端的回調對象實現如下:
          class SumDuplexCallbackHandler:ISumDuplexCallback
          {
              public SumDuplexCallbackHandler(ListBox listBox)
              {
                  m_listBox = listBox;
              }
              private ListBox m_listBox;      
              #region ISumDuplexCallback Members
              public void Equals(int result)
              {
           ClientUtil.IsCompleted = true;
                  m_listBox.Items.Add(string.Format("The result is:{0}", result));
                  m_listBox.Refresh();           
              }     
              #endregion
          }
      當客戶端點擊Start按鈕,調用服務對象的Sum方法后,在服務端會顯示迭代值,而客戶端也開始執行自己的任務,向ListBox控件中添加累加數。一旦服務端運算完畢,就將運算結果通過回調對象傳送到客戶端,全局變量ClientUtil.IsCompleted將被設置為true。如果添加累加值的線程處于sleep狀態,系統就會將結果值添加到ListBox控件中,同時會彈出對話框,決定是否繼續剩下的任務。

      注:本文示例的代碼和實例均在Feb 2006 CTP版本下運行。

      < 未完待續>

      參考:
      1、David Chappell,Introducing Windows Communication Foundation
      2、Microsoft Corporation,WinFX SDK

      posted @ 2006-04-17 12:25  張逸  閱讀(11143)  評論(13)    收藏  舉報
      主站蜘蛛池模板: 国产精品国产三级国产试看| 亚洲熟女乱综合一区二区| 少妇人妻综合久久中文字幕| 日韩精品一二三黄色一级| 色婷婷日日躁夜夜躁| 欧美激情一区二区三区成人| 69精品丰满人妻无码视频a片| 无码A级毛片免费视频下载| 国产精品亚洲国际在线看| 亚洲成在人线AⅤ中文字幕| 久9re热视频这里只有精品免费| 野外做受又硬又粗又大视频√| 国产在线视频不卡一区二区| 极品尤物被啪到呻吟喷水| 国产成人午夜福利院| 亚洲精品乱码久久观看网| 中文字幕无码视频手机免费看| 五月婷婷中文字幕| 制服丝袜中文字幕在线| 国产午夜精品在人线播放| 亚洲成av人片无码天堂下载| 成人午夜大片免费看爽爽爽| 伊人春色激情综合激情网| 无码专区视频精品老司机| 亚洲av午夜福利精品一区二区| 十八禁在线观看视频播放免费| 九九热在线这里只有精品| 亚洲国产精品久久久天堂麻豆宅男 | 亚洲av永久无码精品网站| 一本精品99久久精品77| 国产精品久久久久久亚洲色| 久久亚洲国产成人亚| 午夜福利偷拍国语对白| 久热中文字幕在线精品观| 中文字幕有码日韩精品| 扒开粉嫩的小缝隙喷白浆视频| 人妻少妇精品性色av蜜桃| 永久免费无码av在线网站| 国产综合色在线精品| 伊人激情一区二区三区av| 办公室强奷漂亮少妇同事|