创建自定义的真实代理实现服务的调用
ChannelFactory
3: //其他成员
4: public Uri Address { get; private set; }
5: public MessageVersion MessageVersion { get; private set;
} 6: public IDictionary<string, IClientMessageFormatter> MessageFormatters { get; private set; }
7: public MessageEncoderFactory MessageEncoderFactory { get; private set; }
8:
9: public ServiceChannelProxy(Uri address, MessageVersion messageVersion, MessageEncoderFactory encoderFactory): base(typeof(TChannel))
10:
{
11: this.Address = address;
12: this.MessageVersion = messageVersion;
13: this.MessageEncoderFactory = encoderFactory;
14: this.MessageFormatters = new Dictionary<string, IClientMessageFormatter>();
15: }
16: }
和WcfHttpHandler类似,进行服务调用所需的组件通过相应的只读属性表示。属性MessageEncoderFactory表示消息编码器工厂,而字典类型的MessageFormatters表示基于每个操作的客户端消息格式化器列表,其中的Key为操作的名称。属性Address表示被调用服务的地址。
针对透明代理的方法调用最终都会转移到针对真实真实代理的Invoke方法,所以我们将所有的服务调用操作实现在如下的Invoke方法中。我们首先获取代表当前调用方法的MethodBase上应用的OperationContractAttribute特性,并借此获得操作名称。
根据获取的操作名称从属性MessageFormatters属性中获得基于当前操作的客户端消息格式化器,并将方法调用转化消息。接着根据Address属性表示的服务调用地址创建EndpointAddress对象并将其附加到请求消息中。除此之外,还需要为请求消息添加一些必要的报头(比如
接下来通过消息编码器工厂创建的消息编码器对消息进行编码,并将得到的字节数据通过创建的HttpWebRequest对象发送出去。对于得到的HttpWebResponse,则通过消息编码器进行解码以生成回复消息。回复消息最终通过客户端消息格式化器进行反序列化,得到的对象映射为方法返回值和输出/引用参数返回。
2: {
3: //其他成
4: public override IMessage Invoke(IMessage msg)
5: {
6: IMethodCallMessage methodCall = (IMethodCallMessage)msg;
7:
8: //得到操作名称
9: object[] attributes = methodCall.MethodBase.GetCustomAttributes(typeof(OperationContractAttribute), true);
10: OperationContractAttribute attribute = (OperationContractAttribute)attributes[0];
11: string operationName = string.IsNullOrEmpty(attribute.Name) ? methodCall.MethodName : attribute.Name;
12:
13: //序列化请求消息
14: Message requestMessage = this.MessageFormatters[operationName].SerializeRequest(this.MessageVersion, methodCall.InArgs);
15:
16: //添加必要的WS-Address报头
17: EndpointAddress address = new EndpointAddress(this.Address);
18: requestMessage.Headers.MessageId = new UniqueId(Guid.NewGuid());
19: requestMessage.Headers.ReplyTo = new EndpointAddress("http://www.w3.org/2005/08/addressing/anonymous");
20: address.ApplyTo(requestMessage);
21:
22: //对请求消息进行编码,并将编码生成的字节发送通过HttpWebRequest向服务端发送
23: HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(this.Address);
24: webRequest.Method = "Post";
25: webRequest.KeepAlive = true;
26: webRequest.ContentType = "application/soap+xml; charset=utf-8";
27: ArraySegment<byte> bytes = this.MessageEncoderFactory.Encoder.WriteMessage(requestMessage, int.MaxValue, BufferManager.CreateBufferManager(long.MaxValue, int.MaxValue));
28: webRequest.ContentLength = bytes.Array.Length;
29: webRequest.GetRequestStream().Write(bytes.Array, 0, bytes.Array.Length);
30: webRequest.GetRequestStream().Close();
31: WebResponse webResponse = webRequest.GetResponse();
32:
33: //对HttpResponse进行解码生成回复消息.
34: Message responseMessage = this.MessageEncoderFactory.Encoder.ReadMessage(webResponse.GetResponseStream(), int.MaxValue);
35:
36: //回复消息进行反列化生成相应的对象,并映射为方法调用的返回值或者ref/out参数
37: object[] allArgs = (object[])Array.CreateInstance(typeof(object),methodCall.ArgCount);
38: Array.Copy(methodCall.Args, allArgs, methodCall.ArgCount);
39: object[] refOutParameters = new object[GetRefOutParameterCount(methodCall.MethodBase)];
40: object returnValue = this.MessageFormatters[operationName].DeserializeReply(responseMessage, refOutParameters);
41: MapRefOutParameter(methodCall.MethodBase, allArgs, refOutParameters);
42:
43: //通过ReturnMessage的形式将返回值和ref/out参数返回
44: return new ReturnMessage(returnValue, allArgs, allArgs.Length, methodCall.LogicalCallContext, methodCall);
45: }
46:
47: private int GetRefOutParameterCount(MethodBase method)
48: {
49: int count = 0;
50: foreach (ParameterInfo parameter in method.GetParameters())
51: {
52: if (parameter.IsOut || parameter.ParameterType.IsByRef)
53: {
54: count++;
55: }
56: }
57: return count;
58: }
59:
60: private void MapRefOutParameter(MethodBase method, object[] allArgs,object[] refOutArgs)
61: {
62: List<int> refOutParamPositionsList = new List<int>();
63: foreach (ParameterInfo parameter in method.GetParameters())
64: {
65: if (parameter.IsOut || parameter.ParameterType.IsByRef)
66: {
67: refOutParamPositionsList.Add(parameter.Position);
68: }
69: }
70: int[] refOutParamPositionArray = refOutParamPositionsList.ToArray();
71: for (int i = 0; i < refOutArgs.Length; i++)
72: {
73: allArgs[refOutParamPositionArray[i]] = refOutArgs[i];
74: }
75: }
76: }
定义服务代理工厂
WCF的服务代理对象是通过ChannelFactory
2: {
3: public Uri Address { get; private set; }
4:
5: public ServiceProxyFactory(Uri address)
6: {
7: this.Address = address;
8: }
9: public TChannel CreateChannel()
10: {
11: MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
12: ServiceChannelProxy<TChannel> proxy = new ServiceChannelProxy<TChannel>(this.Address, MessageVersion.Default, encoderFactory);
13: ContractDescription contract = ContractDescription.GetContract(typeof(TChannel));
14: foreach (OperationDescription operation in contract.Operations)
15: {
16: IClientMessageFormatter messageFormatter = (IClientMessageFormatter)ComponentBuilder.GetFormatter(operation, true);
17: proxy.MessageFormatters.Add(operation.Name, messageFormatter);
18: }
19: return (TChannel)proxy.GetTransparentProxy();
20: }
21: }
服务“寄宿”和调用
现在我们创建一个服务寄宿在我们自定义的迷你版本的WCF中。依然采用我们熟悉的计算服务,下面是分别定义的Service.Interface和Service项目中的契约接口定义和服务类型定义。
2: [ServiceContract(Namespace = "http://www.artech.com/")] 3: public interface ICalculator
4: {
5: [OperationContract]
6: double Add(double x, double y);
7: [OperationContract]
8: double Subtract(double x, double y);
9: [OperationContract]
10: double Multiply(double x, double y);
11: [OperationContract]
12: double Divide(double x, double y); 13: }
14:
15: //服务类型
16: public class CalculatorService: ICalculator
17: {
18: public double Add(double x, double y)
19: {
20: return x + y;
21: }
22: public double Subtract(double x, double y)
23: {
24: return x - y;
25: }
26: public double Multiply(double x, double y)
27: {
28: return x * y;
29: }
30: public double Divide(double x, double y)
31: {
32: return x / y;
33: }
34: }
然后我们为Web项目Service中添加一个Global.asax文件,并通过如下的定义让Web应用启动的时候注册寄宿的服务类型CalculatorService和地址(calculatorservice)之间的映射关系。然后在IIS中创建一个Web应用(比如起名为WcfServices)并将物理路径映射为Service项目的根目录。
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: RouteTable.Routes.Register<CalculatorService>("calculatorservice");
6: }
7: }
由于最终处理服务调用请求的WcfHandler是通过WcfHttpModule进行映射的,所以我们需要将WcfHttpModule类型配置在Service项目的Web.config中
2: <system.webServer>
3: <modules>
4: <add name="WcfHttpModule" type="Artech.WcfServices.Service.WcfHttpModule, Artech.WcfServices.Service"/>
5: </modules>
6: </system.webServer>
7: </configuration>
在客户端我们只需要按照如下的方式通过指定正确的调用地址(Web应用地址+在Global.asax文件中添加的路由映射的地址)创建ServiceProxyFactory
2: ServiceProxyFactory<ICalculator> factory = new ServiceProxyFactory<ICalculator>(address);
3: ICalculator proxy = factory.CreateChannel();
4: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2));
5: Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2));
6: Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2));
7: Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2));
上面的代码执行之后,就像你真正调用WCF服务一样,同样可以得到如下的运算结果。
2: x - y = -1 when x = 1 and y = 2
3: x * y = 2 when x = 1 and y = 2
4: x / y = 0.5 when x = 1 and y = 2
源代码下载:http://files.cnblogs.com/artech/wcf-how-to-work.rar
出处:http://artech.cnblogs.com