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


·

浙公网安备 33010602011771号