WCF入門(10)


前言

又墮落了,哎。

公司是做乙方的,工資還湊合,主要是項目基本都已完成,進去就是干維護,體會不到那種從頭徹尾的成就感。項目中具體用了EF+Ado.net+WCF+WPF+(VB.net啊,坑啊,完全不知道是這個東西),整個解決方案有47個項目,是一個國際化的電話保險銷售系統中的一部分。感覺自己是去體驗生活的。這個項目也就是自己學習WCF的原因。

工作方面,等手上的結束了再賣自己一次吧。

第十集 Message Contract in WCF (WCF 的MessageContract特性)

很簡單的一集,講的主要是WCF中的MessageContract特性,具體作用是用來控制WCF 的xml request和response的soap消息格式。可以理解為前面講過的DataContract的升級版。如果我們想在程序上完全控制soap消息的格式,這就是一個很好的解決方案。

視頻上通過修改以前的Demo來演示了一遍他的具體使用。我們也來做一遍。

首先,定義兩個類EmployeeRequest 和 EmployeeInfo

 1     [MessageContract(IsWrapped = true, WrapperName = "EmployeeRequestObj", WrapperNamespace = "http://HelloWcf.com/2015/02/04")]
 2     public class EmployeeRequest
 3     {
 4         [MessageHeader(Namespace = "http://HelloWcf.com/2015/02/04")]
 5         public String LicenseKey { get; set; }
 6 
 7         [MessageBodyMember(Namespace = "http://HelloWcf.com/2015/02/04")]
 8         public int Id { get; set; }
 9     }
10     [MessageContract]
11     public class EmployeeInfo
12     {
13         public EmployeeInfo()
14         {
15 
16         }
17         public EmployeeInfo(Employee emp)
18         {
19             this.ID = emp.Id; this.Name = emp.Name;
20             this.Gender = emp.Gender; this.DOB = emp.DateOfBirth;
21             this.EmployeeType = emp.EmployeeType;
22         }
23         [MessageBodyMember]
24         public int ID { get; set; }
25         [MessageBodyMember]
26         public String Name { get; set; }
27         [MessageBodyMember]
28         public bool Gender { get; set; }
29         [MessageBodyMember]
30         public DateTime DOB { get; set; }
31         [MessageBodyMember]
32         public EmployeeType EmployeeType { get; set; }
33     }
View Code

 區別與以前的DataContract ,這個類是用MessageContract來修飾,里面的屬性用MessageHeader 或者 MessageBodyMember ,作用很明顯,分別對應soap message里面的head 和 body。

LicenseKey在實際的環境中用來標識客戶端的身份,他可以以加密的方式存在,以后會講到。放在Header中,區別於我們Request的body中具體業務邏輯相關的內容。

 

然后修改IEmployeeService這個 ServiceContract

 1     [ServiceContract]
 2     public interface IEmployeeService
 3     {
 4         [OperationContract]
 5         EmployeeInfo GetEmployee(EmployeeRequest req);
 6 
 7         [OperationContract]
 8         void SaveEmployee(EmployeeInfo emp);
 9 
10     }
View Code

 注意,當我們修改了原先的兩個OperationContract 方法的參數后,客戶端就不曉得原先的Employee的概念,他們只知道現在的Request 和 Info。

接口改完之后自然要改寫接口的實現

 1     public class EmployeeService : IEmployeeService
 2     {
 3         public EmployeeInfo GetEmployee(EmployeeRequest er)
 4         {
 5             Employee emp = null;
 6             var connStr = ConfigurationManager.ConnectionStrings["WCFEmployee"].ConnectionString;
 7             using(var conn = new SqlConnection(connStr)) {
 8                 conn.Open();
 9                 var cmd = conn.CreateCommand();
10                 cmd.CommandType = System.Data.CommandType.StoredProcedure;
11                 cmd.CommandText = "spGetEmployeeById";
12                 cmd.Parameters.Add(new SqlParameter("id", er.Id));
13                 var reader = cmd.ExecuteReader();
14                 if(!reader.HasRows) return null;
15                 emp = new Employee();
16                 while(reader.Read()) {
17                     emp.Id = Convert.ToInt32(reader["Id"]);
18                     emp.Name = reader["Name"].ToString();
19                     emp.Gender = Convert.ToBoolean(reader["Gender"]);
20                     emp.DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]);
21                     emp.EmployeeType = (EmployeeType)Convert.ToInt16(reader["EmployeeType"]);
22                 }
23             }
24             return new EmployeeInfo(emp);
25         }
26 
27         public void SaveEmployee(EmployeeInfo emp)
28         {
29             var connStr = ConfigurationManager.ConnectionStrings["WCFEmployee"].ConnectionString;
30             using(var conn = new SqlConnection(connStr)) {
31                 conn.Open();
32                 var cmd = conn.CreateCommand();
33                 cmd.CommandType = System.Data.CommandType.StoredProcedure;
34                 cmd.CommandText = "spSaveEmployee";
35                 cmd.Parameters.Add(new SqlParameter("id", emp.ID));
36                 cmd.Parameters.Add(new SqlParameter("name", emp.Name));
37                 cmd.Parameters.Add(new SqlParameter("gender", emp.Gender));
38                 cmd.Parameters.Add(new SqlParameter("dateOfBirth", emp.DOB));
39                 cmd.Parameters.Add(new SqlParameter("employeeType", (short)emp.EmployeeType));
40                 cmd.ExecuteNonQuery();
41             }
42         }
43 
44     }
View Code

 全是基本的Ado.net的內容,可不看。測試項目用EntityFramework來做挺爽的。記得在VPN掛掉之前下了視頻同個作者的EF教程,等學完這個再相對系統的學習一下EF。

服務端OK了,啟動Host運行一下。(記得要以管理員方式運行host)

客戶端更新一下服務引用,修改一下以前的兩個click事件的代碼

 1     protected void btnGet_Click(object sender, EventArgs e)
 2     {
 3         IEmployeeService client = new EmployeeServiceClient();
 4         try {
 5             var emp = client.GetEmployee(new EmployeeRequest() { LicenseKey = "ABCDEFGHI1234567", Id = Convert.ToInt32(tbId.Text) });
 6             if(emp == null) { tbName.Text = ""; tbDateOfBirth.Text = ""; rbGenderList.SelectedIndex = -1; throw new Exception("沒有找到員工"); }
 7             lbRstMsg.Text = "查找成功";
 8             tbName.Text = emp.Name; tbDateOfBirth.Text = emp.DOB.ToShortDateString(); rbGenderList.SelectedValue = emp.Gender.ToString();
 9         } catch(Exception ex) {
10             lbRstMsg.Text = ex.Message;
11         } finally {
12         }
13     }
14     protected void btnSave_Click(object sender, EventArgs e)
15     {
16         IEmployeeService client = new EmployeeServiceClient();
17         try {
18 
19             var emp = new EmployeeInfo()
20             {
21                 ID = Convert.ToInt32(tbId.Text), Name = Convert.ToString(tbName.Text),
22                 Gender = Convert.ToBoolean(rbGenderList.SelectedValue), DOB = Convert.ToDateTime(tbDateOfBirth.Text)
23             };
24             client.SaveEmployee(emp);
25             lbRstMsg.Text = "保存成功";
26         } catch(Exception ex) {
27             lbRstMsg.Text = ex.Message;
28         }
29     }
View Code

 話說這個WebForm的Demo,寫起來也挺開心的。

這里有個要注意的地方是第三行,實例化client的時候要定義個IEmployeeService 接口類型,不能直接用var來,如果用var就是后面的EmployeeServiceClient類型,他的參數不是ServiceContract中定義的EmployeeRequest,至於為什么,我也不曉得,以后應該會知道的吧。

最后保存,編譯,測試一下。

一切如願,再來看看上集中講的Log。

首先是Request:

 1 <MessageLogTraceRecord>
 2 <HttpRequest xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
 3 <Method>POST</Method>
 4 <QueryString></QueryString>
 5 <WebHeaders>
 6 <VsDebuggerCausalityData>uIDPo34fm0IuoetItvuIdT55jAEAAAAAXsy48FQlpECWY/zesZsAtJ94pCeN9F5IvPTu2MbWQ3AACQAA</VsDebuggerCausalityData>
 7 <SOAPAction>"http://tempuri.org/IEmployeeService/GetEmployee"</SOAPAction>
 8 <Connection>Keep-Alive</Connection>
 9 <Content-Length>291</Content-Length>
10 <Content-Type>text/xml; charset=utf-8</Content-Type>
11 <Accept-Encoding>gzip, deflate</Accept-Encoding>
12 <Expect>100-continue</Expect>
13 <Host>localhost:8080</Host>
14 </WebHeaders>
15 </HttpRequest>
16 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
17 <s:Header>
18 <h:LicenseKey xmlns:h="http://HelloWcf.com/2015/02/04">ABCDEFGHI1234567</h:LicenseKey>
19 <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8080/EmployeeService</To>
20 <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IEmployeeService/GetEmployee</Action>
21 </s:Header>
22 <s:Body>
23 <EmployeeRequestObj xmlns="http://HelloWcf.com/2015/02/04">
24 <Id>1</Id>
25 </EmployeeRequestObj>
26 </s:Body>
27 </s:Envelope>
28 </MessageLogTraceRecord>
View Code

Header中有LicenseKey,body中有Id。

再看Response:

 1 <MessageLogTraceRecord>
 2 <Addressing xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
 3 <Action>http://tempuri.org/IEmployeeService/GetEmployeeResponse</Action>
 4 </Addressing>
 5 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 6 <s:Header>
 7 <ActivityId CorrelationId="7451a535-182c-4b89-8e42-8e312b48d8e0" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">f50e4154-a211-4ed1-b959-9965f22a70d3</ActivityId>
 8 </s:Header>
 9 <s:Body>
10 <EmployeeInfo xmlns="http://tempuri.org/">
11 <DOB>1990-01-02T00:00:00</DOB>
12 <EmployeeType>FullTimeEmployee</EmployeeType>
13 <Gender>true</Gender>
14 <ID>1</ID>
15 <Name>員工1</Name>
16 </EmployeeInfo>
17 </s:Body>
18 </s:Envelope>
19 </MessageLogTraceRecord>
View Code

屬性都是我們在EmployeeInfo中定義的,WrapperName,Namespace等也都ok。

綜上所述,有了MessageContract這個特性,我們對soap信息可以想怎么干就怎么干。

ThankYou。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM