WCF示例及Per-Call和Per-Session配置

這篇文章是幾年前本身學習WCF時的讀書筆記,整理文檔的時候發現了順便放到博客裏面。app

示例是網上流傳很廣的一個訂票系統的示例,因爲在學習過程當中出現了一些錯誤,在此特意把爲何出錯的緣由記錄下來。ide

新建一個WCF服務應用程序學習

在IService1.cs定義服務契約測試

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfDemo
{
    // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼和配置文件中的接口名「IService1」。
    [ServiceContract]
    public interface IService1
    {

        [OperationContract]
        void AddTicket(int count);
        [OperationContract]
        int BuyTickets(int num);
        [OperationContract]
        int GetRemainingNum();

        // TODO: 在此添加您的服務操做
    }


    // 使用下面示例中說明的數據約定將複合類型添加到服務操做。
    [DataContract]
    public class Ticket
    {
        bool boolCount = true;
        int howMany = 10;
        [DataMember]
        /*
        判斷是否還有票
        */
        public bool BoolCalue
        {
            get { return boolCount; }
            set
            {
                if (howMany > 0)
                {
                    boolCount = false;
                }
                else
                {
                    boolCount = true;
                }
            }
        }
        [DataMember]
        public int HowMany
        {
            get { return howMany; }
            set { howMany = value; }
        }

    }
}

在Service1.svc.cs中實現契約服務ui

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfDemo
{
    // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼、svc 和配置文件中的類名「Service1」。
    //[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class Service1 : IService1
    {
        Ticket T = new Ticket();
        public void AddTicket(int count)
        {
            T.HowMany = T.HowMany + count;
        }
        public int GetRemainingNum()
        {
            return T.HowMany;
        }
        public int BuyTickets(int Num)
        {
            if (T.BoolCalue)
            {
                T.HowMany = T.HowMany - Num;
                return 1;
            }
            else
            {
                return 0;
            }
        }
        
    }
}

如今添加宿主程序用於監測服務,添加WinForm項目加入解決方案this

WinForm宿主程序界面以下圖:spa

添加WCF服務應用程序生成的dll文件debug

配置以下圖,在解決方案的屬性裏面須要把啓動項目設置成宿主程序,不然每次F5啓動都會失敗代理

源代碼調試

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        ServiceHost host = null;

        private void button1_Click(object sender, EventArgs e)
        {
            host = new ServiceHost(typeof(WcfDemo.Service1));
            host.Open();
            this.label1.Text = "服務已啓動";

        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (host.State != CommunicationState.Closed)
            {
                host.Close();
            }
            this.label1.Text = "服務已關閉";
        }
    }
}

接下來配置宿主程序的app.config

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
<system.serviceModel> 
<services><!--添加服務--> 
<service name="WcfDemo.Service1" behaviorConfiguration="CalculatorServiceBehavior"> 
<!--name 必須與代碼中的host實例初始化的服務同樣 
behaviorConfiguration 行爲配置 --> 
<host> 
<baseAddresses> 
<!--添加調用服務地址--> 
<add baseAddress="http://localhost:8000/"/> 
</baseAddresses> 

</host> 
<!--添加契約接口 contract="WcfDemo.IService1" WcfDemo.IService1爲契約接口 binding="wsHttpBinding" wsHttpBinding爲經過Http調用.若是下面的節點不存在,那麼默認的綁定方式是basicHttpBinding。--> 
<endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint> 
</service> 

</services> 
<!--定義CalculatorServiceBehavior的行爲--> 
<behaviors> 
<serviceBehaviors> 
<behavior name="CalculatorServiceBehavior"> 
<serviceMetadata httpGetEnabled="true"/> 
<serviceDebug includeExceptionDetailInFaults="false"/> 

</behavior> 

</serviceBehaviors> 

</behaviors> 
</system.serviceModel> 
</configuration>

程序運行結果:

服務啓動後能夠經過app.config中baseAddress節點中的baseAddress地址查看WCF服務

到此爲止服務以及宿主程序都已經建立好了,下面該建立測試客戶機了。

新建個WinForm程序做爲咱們的測試客戶機

界面以下:

購買車票:調用WCF服務的BuyTickets()方法

查詢車票:調用WCF服務的GetRemainingNum()方法

Label用於顯示調用結果

爲項目添加服務引用,地址輸入宿主程序app.config中baseAddress地址

後臺代碼爲

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WCFClient
{
    public partial class Form1 : Form
    {
        ServiceReference2.Service1Client TClient = new ServiceReference2.Service1Client();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int i = TClient.BuyTickets(2);
            if (i == 1)
            {
                this.label1.Text = "購買成功";
            }
            this.label1.Text += "剩餘車票還有:" + TClient.GetRemainingNum().ToString();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.label1.Text = "";
            this.label1.Text = TClient.GetRemainingNum().ToString();
        }
    }
}

點擊購買車票時的結果以下,和網上的示例獲得的結果不同。

使用debug調試發現,每次調用一個方法,Ticket都要從新生成,查詢資料發現,這和服務實例的生命週期有關。在宿主程序的app.config配置裏面,我少寫了

<endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint>

致使默認的綁定方式是basicHttpBinding,而且服務實例的生命週期是Per-Call的,也即每次調用服務方法,服務實例都要從新生成。在宿主程序裏面把app.config修改,加上

<endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint>

從新生成服務端,而後運行客戶端,報錯以下

這是因爲宿主程序的binding方式改變了,可是客戶端的代理類的binding方式沒有作相應的修改致使的,從新把代理類生成一次,如前面的圖所示我從新生成了代理類ServiceReference2。

再次運行客戶端程序而後打擊購買車票,獲得了預期的結果還剩8張車票。使用wsHttpBinding的時候,默認的服務實例生命週期是Per-Session,這樣在一個會話裏面調用服務方法,服務實例不會每次都從新生成。

客戶端app.config以下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8000/" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
                name="BasicHttpBinding_IService1" />
            <endpoint address="http://localhost:8000/" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference2.IService1"
                name="WSHttpBinding_IService1">
                <identity>
                    <userPrincipalName value="Administrator@wmq.kxu.com" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>
相關文章
相關標籤/搜索