zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    使用NPOI导入导出标准Excel
    winform ListView应用之分组、重绘图标、网格线
    在网页中显示CHM
    动态表单(javascript实现)
    批量上传文件时,js验证文件名不能相同
    IE开发人员工具无法使用
    卸载方法 gnu grub version 0.97
    VS.Net 2003/VC6.0常用快捷键集合
    SQL SERVER 与ACCESS、EXCEL的数据转换
    通用获取父节点/子节点/子节点下所有节点ID的存储过程
  • 原文地址:https://www.cnblogs.com/sheldon-lou/p/4273407.html
Copyright © 2011-2022 走看看