65.9K
CodeProject 正在变化。 阅读更多。
Home

美味!使用 DynamicTidyProxy 实现更简洁、更优雅的 WCF 代理用法!

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2015年11月21日

CPOL

2分钟阅读

viewsIcon

6247

使用 DynamicTidyProxy 进行更干净的 WCF 代理使用

这是对我的上一篇(实际上,我的第一篇)文章“Mmmmm… 使用 TidyProxy 实现简洁、优雅的 WCF 代理用法!”的后续,该文章展示了如何安全地使用任意 WCF 代理类。

我日常开发工作的大部分时间都在使用 WCF 服务,主要是 Web 和 MSMQ。经过一些初期问题,一切都运行良好。通常,这些都是内部 Windows 到 Web 的通信,我可以控制客户端和服务器的实现,因此有很多共享类型。

然而,我最近一个快速增长的 Web 服务 API 达到了元数据限制(显然,存在最多 30 个方法的原因!),这意味着 VS2008 或 svcutil 无法可靠地生成可用的代理,而无需更改可能需要生成代理的每台机器的标准配置。

因此,在一次巨大的重构之后,我将一个服务分解为十四个(14 个! – 它怎么变得这么大!)更小、更连贯的服务 – 我现在面临着一项更艰巨的任务,即用较小的服务替换较大的服务。显然,这不会很好,因为我们需要将一个服务引用替换为十四个!由于该服务在多个项目中被使用,这可能会导致我被钉在墙上。

经过一番 Google 搜索、Reflectoring 和检查生成的代码后,我很快意识到,对于我的情况,我根本不需要生成代理!由于所有类型都使用公共库在客户端和服务器之间共享,并且 WCF 配置在配置文件中,我只需要包装 ChannelFactory<>。这个巧妙的类完成了 WCF 中的所有繁重工作,只需要服务契约接口和端点名称!

仿佛魔法一般,代理出现了…

   using System;
   using System.ServiceModel;
    
   namespace MartinOnDotNet.Helpers.WCF
   {
       /// <summary>
       /// Dynamically create a WCF proxy class using the given interface
       /// </summary>
       /// <typeparam name="TServiceContract">The type of the service contract.</typeparam>
       public class DynamicTidyProxy<TServiceContract> : IDisposable
       {    
           /// <summary>
           /// Initializes a new instance of the 
           /// <see cref="DynamicTidyProxy&lt;TServiceContract&gt;"/> class.
           /// </summary>
           public DynamicTidyProxy()
           {
               EndpointName = typeof(TServiceContract).Name;
           }
    
           /// <summary>
           /// Initializes a new instance of the 
           /// <see cref="DynamicTidyProxy&lt;TServiceContract&gt;"/> class.
           /// </summary>
           /// <param name="endpointConfigurationName">Name of the endpoint configuration.
           /// </param>
           public DynamicTidyProxy(string endpointConfigurationName)
           {
               EndpointName = endpointConfigurationName;
           }
    
           /// <summary>
           /// Gets or sets the name of the endpoint.
           /// </summary>
           /// <value>The name of the endpoint.</value>
           public string EndpointName { get; set; }
    
           private ChannelFactory<TServiceContract> _channel;
           private TServiceContract _client;
   
           /// <summary>
           /// Gets the client.
           /// </summary>
           /// <value>The client.</value>
           public TServiceContract Client
           {
               get
               {
                   if (_client == null)
                   {
                       if (!typeof(TServiceContract).IsInterface) 
                       throw new NotSupportedException("TServiceContract must be an interface!");
                       if (string.IsNullOrEmpty(EndpointName)) 
                       throw new NotSupportedException("EndpointName must be set prior to use!");
                       _channel = new ChannelFactory<TServiceContract>(EndpointName);
                       _client = _channel.CreateChannel();
                   }
                   return _client;
               }    
           }    
    
           #region IDisposable Members
    
           /// <summary>
           /// Performs application-defined tasks associated with freeing, 
           /// releasing, or resetting unmanaged resources.
           /// </summary>
           public void Dispose()
           {
               if (_channel != null)
               {
                   _channel.CloseConnection();
               }
               GC.SuppressFinalize(this);
           }
    
           #endregion
       }
   }

我将默认构造函数编码为使用服务契约接口类型名称…所以请确保您的配置文件匹配!

然后,您可以使用以下代码动态创建 WCF 代理:

   using (var proxy = new DynamicTidyProxy<IMagicComplexServiceContract>())       
   {      
     proxy.Client.DoSomeMagicallyComplexOperation();     
   }

无需 svcutil 或 VS2008 服务引用!

这也意味着我不需要花那么多时间进行重构!(至少今天…它迟早会发生 - 我保证)。

整理

您可能已经注意到 DynamicTidyProxy Dispose 方法中的微妙“_channel.CloseConnection()”...这只是一个扩展方法,它包装了所有必需的 WCF 样板代码…

   // <summary>
   /// Safely closes a service client connection.
   /// </summary>
   /// <param name="serviceClient">The service client.</param>
   public static void CloseConnection(this ICommunicationObject serviceClient)
   {
       if (serviceClient == null) return;
       try
       {
           if (serviceClient.State == CommunicationState.Opened)
           {
               serviceClient.Close();
           }
           else
           {
               serviceClient.Abort();
           }
       }
       catch (CommunicationException ex)
       {
           Logging.Logger.Log(ex);
           try
           {
               serviceClient.Abort();
           }
           catch { } //nasty but nothing useful can be found 
                     //by logging this exception as secondary issue
       }
       catch (TimeoutException ex)
       {
           Logging.Logger.Log(ex);
           try
           {
               serviceClient.Abort();
           }
           catch { }//nasty but nothing useful can be found 
                    //by logging this exception as secondary issue
       }
       catch (Exception ex)
       {
           Logging.Logger.Log(ex);
           try
           {
               serviceClient.Abort();
           }
           catch { }//nasty but nothing useful can be found 
                    //by logging this exception as secondary issue
           throw;
    
       }
   }
© . All rights reserved.