复习WCF(7)----- Message类

 

 

Message类是WCF的基本类

客户端与服务端之间的所有通信最终都会产生Message类的实例

无论是用数据协定还是消息协定来包装的一个传递的消息,最终WCF架构都会将它们转换为Message这样的一个类型

通常不会直接用到Message类,一般都是使用数据协定或消息协定

 

那什么情况下会用到Message这个类呢

主要的应用场景是要对传出或传入的消息进行完全的控制才会用到Message类,比如说从磁盘上的文件直接创建消息,而不是序列化.Net Framework对象

所以用Message类就可以实现,可以完全的控制我们的消息,无论是标头还是正文还是什么其他的

 

怎么使用呢

直接将Message作为返回值或者参数就可以了,但是它也有一些限制

1.操作不能具有任何out或ref参数2

2.不能有一个以上的input参数,如果该参数确实存在,就必须为Message类型或消息协定(使用消息协定的操作也只能有一个参数,而且参数类型必须为消息协定)

3.返回值类型必须为Void,Message或消息协定

如图:

 

 

创建简单的消息

Message类提供了一个静态的CreateMessage工厂方法

   所有CreateMessage重载都采用一个类型为MessageVersion的版本参数,该参数指示要用于消息的SOAP和WS-Addressing版本(指定消息的传递是基于什么样的一个SOAP也好什么样的WS-Addressing版本也好,因为我们在使用这个Message来传递消息的时候,最终会转化为这种标准的SOAP的XML,所以他需要传入和接收端使用相同的SOAP协议版本)。一般是直接得到消息的版本输出 例:OperationContext.Current.IncomingMessageVersion

   大多数CreateMessage重载还具有一个字符串参数,该参数指示要用于消息的SOAP操作,稍后例子解释

   可以将版本设置为None以禁用SOAP信封生成(也就是不用标准soap格式来序列化消息了),消息将仅包含正文。但通常情况下是不建议这么做的,毕竟wcf是基于一系列W3C的规范来做的,要想和别的平台交互,还是符合标准的较好。

 

   另一种重载采用一个附加的Object参数,此重载所创建的消息的正文是给定对象的序列化表示

 

   从XML读取器创建消息:有些CreateMessage重载采用一个XmlReader或一个XmlDictionaryReader而不是对象作为正文。比如说,我们要从一个文件里面读到文件的信息,把文件的内容以服务方法的方式传递给客户端,那我们则可以通过这种重载完成需求

 

   创建错误消息:可以使用某些CreateMessage重载创建SOAP错误消息。其中一个最简单的重载采用一个用于描述错误的MessageFault对象作为参数

 

 

代码演示与讲解(此演示中包含很多例子,代码暂时不用看,解释的时候在看) 项目工程图:

 

服务端IService1.cs:

using System.ServiceModel;
using System.ServiceModel.Channels;

namespace service
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
    [ServiceContract(Namespace = "http://ClassLibrary1")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
        [OperationContract]
        Message Sum(Message request);
        [OperationContract]
        Message GetFirst();
        [OperationContract]
        Message GetData();
        [OperationContract]
        Message GetDataStream();
        [OperationContract]
        Message GetDataFault();
    }
}

Service1.cs:

using System;
using System.Globalization;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Xml;

namespace service
{
    public class CalculatorService : ICalculator
    {
        public double Add(double n1, double n2)
        {
            return n1 + n2;
        }

        public double Subtract(double n1, double n2)
        {
            return n1 - n2;
        }

        public double Multiply(double n1, double n2)
        {
            return n1 * n2;
        }

        public double Divide(double n1, double n2)
        {
            return n1 / n2;
        }

        public Message Sum(Message request)
        {
            int sum = 0;
            string text = "";


            XmlReader body = request.GetReaderAtBodyContents();

            while (body.Read())
            {
                text = body.ReadString().Trim();
                if (text.Length > 0)
                {
                    sum += Convert.ToInt32(text, CultureInfo.InvariantCulture);
                }
            }
            body.Close();

            Message response = Message.CreateMessage(request.Version, "http://ClassLibrary1/ICalculator/SumResponse", sum);
            return response;
        }

        public Message GetFirst()
        {
            MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver, "http://ClassLibrary1/ICalculator/GetFirstResponse");
        }

        public Message GetData()
        {
            Person p = new Person();  //创建一个消息对象类型(Person是一个消息协定)
            p.name = "wang";
            p.age = 20;
            MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver, "http://ClassLibrary1/ICalculator/GetDataResponse", p);//将这个person对象传给客户端 客户端就能拿到它
        }

        public Message GetDataStream()
        {
            FileStream stream = new FileStream(@"C:\myfile.xml", FileMode.Open);
            XmlDictionaryReader xdr =
                   XmlDictionaryReader.CreateTextReader(stream,
                               new XmlDictionaryReaderQuotas());
            MessageVersion ver =
                OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver, "http://ClassLibrary1/ICalculator/GetDataStreamResponse", xdr);
        }


        public Message GetDataFault()
        {
            FaultCode fc = new FaultCode("Receiver");
            MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver, fc, "Bad data", "http://ClassLibrary1/ICalculator/GetDataFaultResponse");
        }

    }

    [MessageContract(WrapperNamespace = "http://ClassLibrary1")]
    public class Person
    {
        [MessageBodyMember]
        public string name;
        [MessageBodyMember]
        public int age;
    }
}

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="service.CalculatorService" behaviorConfiguration="service.Service1Behavior">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8732/Design_Time_Addresses/service/CalculatorService/" />
          </baseAddresses>
        </host>

        <endpoint address ="" binding="wsHttpBinding" contract="service.ICalculator">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="service.Service1Behavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

 

客户端Program.cs文件:

using System;
using System.Globalization;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Xml;
using System.IO;
using System.Text;
using client.ServiceReference1;

namespace client
{
    class Program
    {
        static void Main()
        {
            CalculatorClient clientx = new CalculatorClient();

            double value1 = 100.00D;
            double value2 = 15.99D;
            double result = clientx.Add(value1, value2);
            Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);

            value1 = 145.00D;
            value2 = 76.54D;
            result = clientx.Subtract(value1, value2);
            Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);

            value1 = 9.00D;
            value2 = 81.25D;
            result = clientx.Multiply(value1, value2);
            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);

            value1 = 22.00D;
            value2 = 7.00D;
            result = clientx.Divide(value1, value2);
            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
            Console.WriteLine("");
            Console.WriteLine("***********************************分割***************************************");
            Console.WriteLine("");

            int[] values = { 1, 2, 3, 4, 5 };  //Sum方法需要的整型数组
            using (new OperationContextScope(clientx.InnerChannel))
            {
                Message request = Message.CreateMessage(OperationContext.Current.OutgoingMessageHeaders.MessageVersion,
                    "http://ClassLibrary1/ICalculator/Sum", values); //将数组传进去

                Message reply = clientx.Sum(request); //然后调用Sum方法 将request这个消息做为参数
                int sum = reply.GetBody<int>();  //通过GetBody得到返回消息中所求出的和的值 因为知道是int类型 所以使用GetBody<int>();
                Console.WriteLine("Sum(1,2,3,4,5) = {0}", sum); //然后输出它得出的和


                // demo 1
                //Message Reply1 = client.GetFirst();
                //Console.WriteLine(Reply1.ToString());
                
                // demo 2
                //Message reply1 = clientx.GetData();  //调用GetData拿到Message对象
                //Console.WriteLine(reply1.ToString());
                //Person p = reply1.GetBody<Person>(); //GetBody这个泛型方法直接转换拿到我们想要的Person类型对象
                //Console.WriteLine(p.name + "    " + p.age.ToString()); //输出person的值

                // demo 3
                Message reply1 = clientx.GetDataStream();
                Console.WriteLine(reply1.ToString());
                FileStream stream = new FileStream(@"c:\log.xml", FileMode.Create);
                XmlDictionaryWriter xdw =
                    XmlDictionaryWriter.CreateTextWriter(stream);
                //reply1.WriteBodyContents(xdw);
                //reply1.WriteBody(xdw);
                reply1.WriteMessage(xdw);
                xdw.Flush();

                // demo 4
                //try
                //{
                //    Message reply1 = clientx.GetDataFault();
                //    Console.WriteLine(reply1.ToString());
                //}
                //catch (FaultException e)
                //{
                //    Console.WriteLine(e.ToString());
                //}


                // demo 5
                //Message reply1 = clientx.GetDataStream();
                ////Copy the message to a buffer.
                //MessageBuffer mb = reply1.CreateBufferedCopy(65536);

                ////Log to a file.
                //FileStream stream = new FileStream(@"C:\log.xml", FileMode.Append);
                //mb.WriteMessage(stream);
                //stream.Flush();

                // demo 6
                //Message reply1 = clientx.GetData();
                //Console.WriteLine(reply1.ToString());
                //foreach (MessageHeaderInfo mhi in reply1.Headers)
                //{
                //    Console.WriteLine(mhi.Name);
                //}


            }

            //Closing the client gracefully closes the connection and cleans up resources
            clientx.Close();

            Console.WriteLine();
            Console.WriteLine("Press <ENTER> to terminate client.");
            Console.ReadLine();
        }

    }
}

 

从这里就应该能看出有6个例子吧。。。。。。

 

关于CreateMessage方法里的字符串参数:

大家可以看到服务方法Sum(),GetFirst(),GetData()等等的实现,都有一段URL似的字符串,如:http://ClassLibrary1/ICalculator/SumResponse,它是一个命名空间

它与生成的代理类的ReplyAction对应 如图:

如果ReplyAction与服务方法中的那个字符串不匹配,则调用此服务则会发生异常。而Action是默认的(命名空间+协定+操作方法名),一开始就指定的命名空间 如图:

 

关于客户端Program.cs中的using块:

大家可以看到program中有这样一段代码

using (new OperationContextScope(client.InnerChannel))
            {
                Message request = Message.CreateMessage(OperationContext.Current.OutgoingMessageHeaders.MessageVersion,
                    "http://ClassLibrary1/ICalculator/Sum", values);

...

...

通过Client.InnerChannel获得到客户端的通道,用using块监视这个通道是否全部执行完了,执行完了后它就可能会有一个资源回收的操作

 

接下来解释具体的demo

demo1:

demo1对应的GetFirst服务方法的具体实现是个空的,只是返回了一个message,我们就看看message是什么

这就是GetFirst返回的消息内容,可以看到这个信封里面只有一个Header(Envelope翻译过来就是信封),而Header返回的消息的ReplyAction

 

然后看看Sum这个服务方法,它有个Message的返回值和参数,它做了一个求和的动作,具体的代码意义如下:

首先,这个message传入以后呢,message有个GetReaderAtBodyContents()的方法,以读取器的方式把message的内容拿到

拿到之后循环,它有值的话就强制转换成整型,然后再求和,然后将求出的和的值sum作为message的消息返回出去

这样子的话,我们这个消息可以在客户端以一个整型数组的方式传进来就好了

 

demo2:

demo2是 从一个对象类型创建消息

如果有人是直接复制的代码测试  那肯定会在person这里出错 找不到person这个类 你们肯定会很奇怪为什么代理类没有生成person呢?

其实很简单 如果你在服务里加个方法 比如:void xxx(Person p); 这样就会生成它了

可能是VS的设计问题吧  硬是要使用到这个协定才会生成它~~~

按道理说结果是这样子:

结果我的出错了  不知道是什么原因  好蛋疼啊 谁知道的话告诉我一下是为什么报错

 

demo3:

demo3是读取文件内容创建消息传给客户端

myfile.xml是本来就存在的,而log.xml是后面调用服务方法生成的。

 

demo4:

我们来看看SOAP的内容

 

关于读消息和写消息

所以 WriteBodyContents是输出body内的正文,WriteBody是输出body内正文(包括body和命名空间),WriteMessage是输出整个SOAP消息

 

demo5:

 

demo6:

Message类还提供了很多属性,以便我们访问正文之外的与消息有关的信息。但前提是,一旦关闭了这个消息,将无法调用这些属性,所以我们要在消息活动时调用

 

 

 

·

 

 

posted @ 2013-02-25 19:02  韬韬韬你羞得无礼  Views(282)  Comments(0)    收藏  举报