1. 程式人生 > >微軟 WCF的幾種寄宿方式,寄宿IIS、寄宿winform、寄宿控制臺、寄宿Windows服務

微軟 WCF的幾種寄宿方式,寄宿IIS、寄宿winform、寄宿控制臺、寄宿Windows服務

創建 src 體驗 ash pos 如果 理解 文件 .get

WCF寄宿方式是一種非常靈活的操作,可以在IIS服務、Windows服務、Winform程序、控制臺程序中進行寄宿,從而實現WCF服務的運行,為調用者方便、高效提供服務調用。本文分別對這幾種方式進行詳細介紹並開發例子進行說明,以求大家對WCF寄宿的方式進行全面的認識和了解。

1、 WCF服務的IIS服務寄宿

我在我前面幾篇WCF開發框架的介紹文章中,介紹過了WCF常用的一種寄宿方式,IIS服務寄宿。這種寄宿方式是最為方便的方式,而且由於服務只需要IIS運行就能自動運行起來,因此廣為使用。

創建這種方式IIS寄宿方式的,只需要在解決方案裏面,添加WCF服務應用程序,就可以生成這種的服務模塊了。

技術分享

這個是一個基於Web的應用程序,創建項目後會生成一個Service1.svc的服務頁面,以及相關的WCF服務接口和實現,如下圖所示。

技術分享

這個就是簡單的WCF服務,當然如果是復雜的實際應用,會考慮和數據庫打交道,而且可能項目會分成幾個進行管理,從而實現更好的邏輯分離操作。

2、 創建WCF服務庫為多種寄宿做準備

除了上面常用的IIS服務寄宿,一般還會有各種各樣的寄宿方式,不過如果采用其他方式的寄宿方式,一般會把WCF服務和寄宿方式進行項目的分離,實現更好的重用操作,特別WCF需要考慮多種寄宿方式的情況下。下面是WCF服務庫和WCF服務應用程序的介紹說明,先了解一下基礎。

WCF服務庫,可以認為是一個包含WCF服務以及契約定義的類庫。這裏WCF服務庫還不能直接運行,你可以在其他項目裏引用,在宿主裏啟用托管這個庫。

而WCF應用程序,是一個可以執行的程序,它有獨立的進程,WCF服務類契約的定義,可以直接看到運行的效果。此項目模板應該是基於IIS托管的程序。

前者一般考慮WCF服務設計的時候,服務類的定義為單獨的庫,可以為其它項目使用。提高代碼的復用性。後者在開發基於IIS托管的WCF服務程序時,比較多見,自學的時候也可以使用這種類型。當然你也可以修改這些代碼,比如把WCF服務程序裏的類,移到一個單獨的類庫裏。

創建WCF服務庫,可以理解為我們開發.NET程序時創建的一個類庫模塊,不含界面,如下所示,創建一個WCF服務庫。

技術分享

確定後就只有一個示例服務Service1生成了。

技術分享

這裏,我們為了演示,就不修改任何它們的代碼,原始的代碼如下所示。

技術分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary
{
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}
技術分享

3、 WCF服務的控制臺程序寄宿

這種也是一種常見的WCF服務寄宿方式,通過啟動一個類似DOS窗口的控制臺軟件,實現WCF服務的動態寄宿,關閉控制臺程序,服務就自然終止。

這種方式很簡單,創建一個控制臺程序,然後添加WCF服務類庫的項目應用,在Main函數裏面添加下面代碼即可實現。

技術分享
namespace WcfService_HostConsole
{
    class Program
    {
        static void Main(string[] args)
        {            
            try
            {
                ServiceHost serviceHost = new ServiceHost(typeof(Service1));
                if (serviceHost.State != CommunicationState.Opened)
                {
                    serviceHost.Open();
                }

                Console.WriteLine("WCF 服務正在運行......");
                Console.WriteLine("輸入回車鍵 <ENTER> 退出WCF服務");
                Console.ReadLine();
                serviceHost.Close();

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}
技術分享

技術分享

4、 WCF服務的Winform程序寄宿

和控制臺程序一樣,我們創建一個Winform項目,然後在窗體啟動代碼裏面添加寄宿方式的代碼即可,為了較好的響應體驗,可以使用後臺線程程序進行服務啟動,如下所示。

技術分享
namespace WcfService_HostWinform
{
    public partial class FrmMain : Form
    {
        ServiceHost serviceHost = null;
        BackgroundWorker worker = null;

        public FrmMain()
        {
            InitializeComponent();

            worker = new BackgroundWorker();
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);

            if (!worker.IsBusy)
            {
                tssTips.Text = "正在啟動......";
                lblTips.Text = tssTips.Text;
                worker.RunWorkerAsync();
            }
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                serviceHost = new ServiceHost(typeof(Service1));
                if (serviceHost.State != CommunicationState.Opened)
                {
                    serviceHost.Open();
                }

                e.Result = "正常";
            }
            catch (Exception ex)
            {
                e.Result = ex.Message;
            }
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result != null)
            {
                if (e.Result.ToString() == "正常")
                {
                    tssTips.Text = "服務正在進行偵聽......";
                }
                else
                {
                    tssTips.Text = string.Format("錯誤:{0}", e.Result);
                }

                lblTips.Text = tssTips.Text;
            }
        }
        ...........................
    }
}
技術分享

當然為了防止Winform程序被不小心關閉,可以添加托盤代碼,把程序縮小到托盤圖標裏面。

技術分享

5、 WCF服務的Windows 服務程序寄宿

這種方式的服務寄宿,和IIS一樣有一個一樣的優點,系統啟動後,WCF服務也會跟著啟動了,不用人工幹預,也是一種較好的寄宿方式。

為了實現這種方式的寄宿,我們創建一個控制臺程序,然後添加響應的Window服務和安裝程序類

技術分享

然後在服務類啟動裏面添加WCF的寄宿代碼,如下所示。

技術分享
    public class ServiceManager : ServiceBase 
    {
        private static object syncRoot = new Object();//同步鎖
        private ServiceHost serviceHost = null; //寄宿服務對象

        public ServiceManager()
        {
            this.ServiceName = Constants.ServiceName;
        }

        /// <summary>
        /// 設置具體的操作,以便服務可以執行它的工作。
        /// </summary>
        protected override void OnStart(string[] args)
        {
            try
            {
                serviceHost = new ServiceHost(typeof(Service1));
                if (serviceHost.State != CommunicationState.Opened)
                {
                    serviceHost.Open();
                }
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
            }

            LogTextHelper.Info(Constants.ServiceName + DateTime.Now.ToShortTimeString() + "已成功調用了服務一次。");

            LogTextHelper.Info(Constants.ServiceName + "已成功啟動。");
        }
技術分享

為了實現通過該控制臺程序實現參數化安裝和卸載服務,我們需要攔截控制臺的參數,並進行相應的操作,如下所示。

技術分享
        /// <summary>
        /// 應用程序的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            ServiceController service = new ServiceController(Constants.ServiceName);

            // 運行服務
            if (args.Length == 0)
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] { new ServiceManager() };
                ServiceBase.Run(ServicesToRun);
            }
            else if (args[0].ToLower() == "/i" || args[0].ToLower() == "-i")
            {
                #region 安裝服務
                if (!ServiceIsExisted(Constants.ServiceName))
                {
                    try
                    {
                        string[] cmdline = { };
                        string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location;

                        TransactedInstaller transactedInstaller = new TransactedInstaller();
                        AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline);
                        transactedInstaller.Installers.Add(assemblyInstaller);
                        transactedInstaller.Install(new System.Collections.Hashtable());

                        TimeSpan timeout = TimeSpan.FromMilliseconds(1000 * 10);
                        service.Start();
                        service.WaitForStatus(ServiceControllerStatus.Running, timeout);
                    }
                    catch (Exception ex)
                    {
                        LogTextHelper.Info(ex);
                        throw;
                    }
                }
                #endregion
            }
            else if (args[0].ToLower() == "/u" || args[0].ToLower() == "-u")
            {
                #region 刪除服務
                try
                {
                    if (ServiceIsExisted(Constants.ServiceName))
                    {
                        string[] cmdline = { };
                        string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location;

                        TransactedInstaller transactedInstaller = new TransactedInstaller();
                        AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline);
                        transactedInstaller.Installers.Add(assemblyInstaller);
                        transactedInstaller.Uninstall(null);
                    }
                }
                catch (Exception ex)
                {
                    LogTextHelper.Info(ex);
                    throw;
                } 
                #endregion
            }
        }
技術分享

編譯程序成功後,我們添加兩個批處理的DOS腳本來實現執行程序的自動安裝和卸載,如下所示。

安裝腳本

"WcfService_HostWinService.exe" -i
pause

卸載腳本

"WcfService_HostWinService.exe" -u
pause

技術分享

順利執行腳本後,服務列表裏面就增加一個服務項目了。

技術分享

卸載操作,直接執行腳本就會卸載服務,非常方便哦。

6、 WCF服務的Web寄宿

當然,除了以上幾種方式,這種以WCF服務庫的方式,也可以在Web方式進行寄宿(IIS方式),這種方式更簡單,添加一個後綴名的svc的文件,只需要一行代碼即可,如下所示。

技術分享

7、 使WCF服務支持GET方式調用

有時候,我們為了需要,可能通過一個小程序發布一個服務,然後供其他程序進行調用,可能是Web,也可以是Winform,但是我們是想提供一個基於HTTP,GET或者POST方式來實現接口的調用的,例如,提供一個JSON格式或者文本格式的內容返回操作。

如果是整合在Winform裏面,那麽我們在Winform裏面添加一個WCF的項,修改裏面的代碼就可以了,如下所示。

技術分享

首先要在使用GET方式的WCF服務接口的添加說明。如果是POS方式,增加設置有點不同([WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)])。

技術分享
namespace WcfServiceForWinform
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void DoWork();

        [OperationContract]
        [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]
        string GetData(int value);
    }
}
技術分享

第二,在實現類裏面添加相應的設置

技術分享
namespace WcfServiceForWinform
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Service1 : IService1
    {
        public void DoWork()
        {
        }

        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }
}
技術分享

配置文件如下所示,顏色標註特別的要註意:

技術分享
<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true"/>
  </system.web>

  <system.serviceModel>
    <services>
      <service name="WcfServiceForWinform.Service1"  behaviorConfiguration="ServiceConfig">
        <endpoint address="" binding="webHttpBinding"
                  bindingConfiguration="webHttpBindingWithJsonP" behaviorConfiguration="webHttpBehavior"
                  contract="WcfServiceForWinform.IService1">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9000/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>

    <bindings>
      <webHttpBinding>
        <binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true"  />
      </webHttpBinding>
    </bindings>
    
    <behaviors>
      <endpointBehaviors>
        <behavior name="webHttpBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
      
      <serviceBehaviors>
        <behavior name="ServiceConfig">
          <serviceMetadata httpGetEnabled="True" policyVersion="Policy15"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
技術分享

運行Winform程序,啟動了WCF服務後,我們可以在瀏覽器(Chrome)上進行操作,如下結果所示。

技術分享

從上圖我們可以看到,這個通過Winform啟動起來的WCF服務,連接也能通過GET方式進行接口調用了,接口可以通過參數進行傳遞,對於一些方便傳輸數據的接口如JSON接口,就是一種非常方便的調用了。

微軟 WCF的幾種寄宿方式,寄宿IIS、寄宿winform、寄宿控制臺、寄宿Windows服務