加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]

发布时间:2020-12-16 09:04:17 所属栏目:asp.Net 来源:网络整理
导读:在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道;当遇到某些异常,我们需要强行中止(Abort)信道,相关的原理,可以参考我的文章《服务代理不能得到及时关闭会有什么后果?》。在真正的企业级开发中,正如我们一般

在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道;当遇到某些异常,我们需要强行中止(Abort)信道,相关的原理,可以参考我的文章《服务代理不能得到及时关闭会有什么后果?》。在真正的企业级开发中,正如我们一般不会让开发人员手工控制数据库连接的开启和关闭一样,我们一般也不会让开发人员手工去创建、开启、中止和关闭信道,这些工作是框架应该完成的操作。这篇文章,我们就来介绍如果通过一些编程技巧,让开发者能够无视“信道”的存在,像调用一个普通对象一样进行服务调用。

一、正常的服务调用方式

如果通过ChannelFactory<TChannel>创建用于服务调用的代理,下面的代码片段描述了客户端典型的服务调用形式:将服务调用在基于代理对象的using块中,并通过try/catch进一步对服务调用操作进行异常处理。当TimeoutException或者CommunicationException被捕获后,调用Abort方法将信道中止。当程序执行到using的末尾,Dispose方法会进一步调用Close方法对信道进行关闭。

class Program
{
    static void Main(string[] args)
    {
        using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
        {
            ICalculator calculator = channelFactory.CreateChannel();
            using (calculator as IDisposable)
            {
                try
                {
                    Console.WriteLine("x + y = {2} when x = {0} and y = {1}",1,2,calculator.Add(1,2));
                }
                    (calculator as ICommunicationObject).Abort();
                    throw;
                }
                {
throw;
            }
        }
?
        Console.Read();
    }
}

二、借助通过Delegate实现异常处理和服务代理的关闭

虽然上面的编程方式是正确的服务调用方式,但是在真正的应用中,如果在每处进行服务调用的地方都采用上面的方式,在我看来是不能容忍的。这不但会让你的程序显得臃肿不堪,而且带来非常多重复的代码,此外频繁创建ChannelFactory<TChannel>对性能也会有影响。我们可以通过一些公共个方法实现对重复代码(ChannelFactory<TChannel>的创建,服务调用的创建、中止和关闭,以及异常处理)。为此我创建了如下一个ServiceInvoker类型,通过两个重载的Invoke方法实现对目标服务的调用。

   1: using System;
   3: using System.ServiceModel;
   5: {
   7:     {
   9:         object syncHelper = new object();
  11:         static ChannelFactory<TChannel> GetChannelFactory<TChannel>(string endpointConfigurationName)
  13:             ChannelFactory<TChannel> channelFactory = null;
  15:             {
  17:             }
  19:             if (null == channelFactory)
  21:                 channelFactory = new ChannelFactory<TChannel>(endpointConfigurationName);
  23:                 {
  25:                 }
  27:             return channelFactory;
  29:? 
  31:         {
  33:             null == channel)
  35:                 throw new ArgumentException("The proxy is not a valid channel implementing the ICommunicationObject interface","proxy");
  37:             try
  39:                 action(proxy);
  41:             catch (TimeoutException)
  43:                 channel.Abort();
  46:             catch (CommunicationException)
  48:                 channel.Abort();
  51:             finally
  53:                 channel.Close();
  55:         }
  57:         static TResult Invoke<TChannel,TResult>(Func<TChannel,TResult> function,TChannel proxy)
  59:             ICommunicationObject channel = proxy as ICommunicationObject;
  61:             {
  63:             }
  66:               return  function(proxy);
  68:               69:             {
  71:                   72:             }
  75:                 channel.Abort();
  78:             finally
  80:                 channel.Close();
  82:         }
  84:         string endpointConfigurationName)
  86:             Guard.ArgumentNotNullOrEmpty(endpointConfigurationName,1)">"endpointConfigurationName");
  88:         }
  90:           91:         {
  93:             return Invoke<TChannel,TResult>(function,1)" id="lnum94">  94:         }        
  96: }

?

处于对性能的考虑,避免对ChannelFactory<TChannel>的频繁创建,通过一个字典对象将创建出来的ChannelFactory<TChannel>缓存起来;两个Invoke方法中,服务的调用通过两个Delegate对象(Action<TChannel>和Func<TChannel,TResult>)表示,另一个参数表示终结点的配置名称。那么这时的服务调用就会变得相当简单:

using Artech.WcfServices.Contracts;
class Program
   9:         {
  11:             Console.WriteLine(  12:             Console.Read();
  14:     }
   3: {
   5:     {
   7:         {get; private set;}
public ServiceInvoker(  10:         {
  12:             this.EndpointConfigurationName = endpointConfigurationName;
  15:         void Invoke(Action<TChannel> action)
  17:             Invoke<TChannel>(action,1)">this.EndpointConfigurationName);
  19:? 
  21:         {
  23:         }
  25: }

通过传入终结点配置名称创建ServiceInvoker<TChannel>对象,直接通过调用基类的静态方法实现了两个Invoke方法。

在分层设计中,为每一个层定义的组件创建基类是一个很常见的设计方式。在这里,假设所有的服务代理类型均继承自基类:ServiceProxyBase<TChannel>,泛型类型为服务契约类型。同样通过传入终结点配置名称创建服务代理,并借助于通过Invoker属性表示的ServiceInvoker<TChannel>对象进行服务的调用。ServiceProxyBase<TChannel>定义如下:

2: {
   4:     {
   6:         { get; private set; }
public ServiceProxyBase(  10:             Guard.ArgumentNotNullOrEmpty(endpointConfigurationName,1)" id="lnum11">  11:             this.Invoker = new ServiceInvoker<TChannel>(endpointConfigurationName);
  13:     }
using Artech.Lib;
namespace Artech.WcfServices.Clients
   5:     class CalculatorProxy : ServiceProxyBase<ICalculator>,ICalculator
   7:         public CalculatorProxy():base(Constants.EndpointConfigurationNames.CalculatorService)
   9:? 
  11:         {
  15:? 
  17:     {
  19:         {
  21:         }
  23: }

那么现在服务代理的消费者(一般是Presenter层对象),就可以直接实例化服务代理对象,并调用相应的方法(这里的方法与服务契约方法一致)即可,所有关于服务调用的细节均被封装在服务代理中。

int result = calculatorProxy.Add(1,2);
  13:             Console.Read();
  15:     }