上篇回顾
上篇提出了个思考,WCF的契约是否可以也基于配置,而不是必须要生成类型,并且从WCF的原理出发点阐述了其可能性,最后提出了XCF的概念。
经过2周的努力,终于实现了一个原形,这一篇里面就讲一下怎么做一个XCF的原形。
实现基础
首先,WCF客户端本身就支持发送任何消息。听起来很不可思议是吧?这就是大家的思维被WCF框架的思维定势所限制,反而把这些基础或者说原始的用法而忽略了。
来看看一个关键接口:IRequestChannel
当然,WCF的关键接口不止这一个,但是对付那些通常的基于tcp/http的WCF服务而言,这个接口就足够了。使用这个接口可以向tcp/http的wcf服务发送任何消息。当然,这里有个问题,任意消息必须要符合服务的协议,否则,服务端将返回错误。
准备服务端
为了方便试验,先简单的准备一个服务端契约:
[ServiceContract(Namespace = "urn:test")] public interface IEchoService { [OperationContract(Action = "Echo")] Person Echo(Person p); }
[DataContract(Namespace = "urn:test")] public class Person { [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } }
以及一个简单的实现:
public class EchoService : IEchoService { public Person Echo(Person p) { Console.WriteLine("Name={0},Age={1}", p.Name, p.Age); return p; } }
以及配置(加上wcf的日志):
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IEchoService"> <security mode="None" /> </binding> </wsHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="WcfHost.EchoServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="WcfHost.EchoServiceBehavior" name="WcfHost.EchoService"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IEchoService" contract="WcfHost.IEchoService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:12345/EchoService/" /> </baseAddresses> </host> </service> </services> <diagnostics> <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000"/> </diagnostics> </system.serviceModel> <system.diagnostics> <sources> <source name="System.ServiceModel.MessageLogging"> <listeners> <add name="messages" type="System.Diagnostics.XmlWriterTraceListener" initializeData="messages.log" /> </listeners> </source> </sources> </system.diagnostics> </configuration>
在这里需要注意的是,要去除安全的部分,因为有安全的wcf会修改消息体,使用编码过的消息体,为原形带来不小的麻烦。
标准客户端
作为对比,先准备一个标准的客户端,过程当然是按照标准的添加服务引用。
然后,准备上一个标准的调用:
Svc.EchoServiceClient client = new WcfNormalClient.Svc.EchoServiceClient(); var resp = client.Echo(new WcfNormalClient.Svc.Person { Name = "abc", Age = 12, }); client.Close(); Console.WriteLine("Name={0},Age={1}", resp.Name, resp.Age);
准备好客户端和服务端后,来一次模拟调用,再查看wcf日志,可以找到:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">Echo</a:Action> <a:MessageID>urn:uuid:fb825687-ad4c-4eab-8b65-0f7c78b9f845</a:MessageID> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <a:To s:mustUnderstand="1">http://localhost:12345/EchoService/</a:To> </s:Header> <s:Body> <Echo xmlns="urn:test"> <p xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Age>12</Age> <Name>abc</Name> </p> </Echo> </s:Body> </s:Envelope>
这就是整个请求消息的消息体,也就是说,要实现Xcf的话,必须要完美的模拟这一请求。
Xcf类库准备
略:文件下载。
Xcf的使用
首先,将类库解压并放在项目中,为了请求之前准备好的wcf服务,需要准备这样一个test.xml:
<?xml version="1.0" encoding="utf-8" ?> <Echo xmlns="urn:test"> <p> <Age>23</Age> <Name>def</Name> </p> </Echo>
这一段的内容与之前抓下来的那个消息中的Body部分一致。
准备工作做好了,来show一下如何让这个原形调用到服务:
using (var f = new XcfChannelFactory(new WSHttpBinding())) using (var c = f.Create(new Uri("http://localhost:12345/EchoService/"))) using (var reader = XmlReader.Create("Test.xml")) using (var resp = c.Request("Echo", reader)) using (var respBody = resp.GetBody()) { Console.WriteLine(respBody.ReadInnerXml()); }
其中,f是工程,c则是通道,reader用于获得一个请求的body,而请求由action和body两部分组成,resp用于保存返回消息,其中的respBody代表着resp消息中的主体。
运行一下,可以在客户端显示出返回的消息,服务端也显示了请求的Name和Age,并且可以在日志中找到相应的请求和响应消息体。
到目前为止,原形已经很好的工作了,但是,不能更改请求的Wcf调用,显然是不实用的。别急,如何让这个请求xml活起来,就是下一篇的议题了。