Apply SOA Design Patterns with WCF (3) Automatic Service Locating (自动化服务定位)

Original (原创) by Teddy’s Knowledge Base

Content (目录)

(1) WCF Configuration Centralization (WCF配置集中管理)

(2) WCF Automatic Deployment (WCF自动化部署)

(3) WCF Automatic Service Locating (WCF自动化服务定位)

(4) WCF Database Paging & Sorting (WCF数据库分页和排序)

(5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)

(6) 1 + 2 + 3 + 4 + 5 = ?

English Version

摘要

本文提供一种在WCF服务消费应用程序中通过与服务提供应用程序共享WCF服务契约接口来自动化定位WCF服务实现的方案。

正文

什么是服务定位?

服务定位的意思是服务的消费者给定服务的接口类型,由一个称作服务定位器的对象返回这个服务接口的实现类,这样,服务的消费者对服务的实现类就没有依赖关系。服务定位器这个设计模式的思路诞生于JAVA世界。这个模式又发展成了IoC容器模式。Martin Folwer对这个设计模式已经讨论很多了

WCF的ChannelFactory类实现了服务定位器设计模式

实际上,WCF的ChannelFactory类也实现了服务定位器模式,它提供了一个接受一个端点配置名称的构造函数,通过这个名称,ChannelFactory就能从应用程序配置文件中读取配置信息,构造绑定信息。构造了ChannelFactory实例以后,就能调用它的CreateChannel()方法来创建服务契约的代理了。一个代理其实是一个动态实现的服务契约的实现类或子类。下面是一个使用ChannelFactory的示例:

1     ChannelFactory<IRequestChannel> factory 
2 
3       = new ChannelFactory<IRequestChannel>("MyEndpoint");
4 
5     IRequestChannel proxy = factory.CreateChannel();
6 
7     //using proxy instance…

和ServiceHost类类似,ChannelFactory类也提供接受Binding类实例的构造函数。所以,也可以用编程方式构造一个Binding类的实例来构造一个ChannelFactory。

包装ChannelFactory类

ChannelFactory类有下面这些限制:

  • 作为一个服务定位器的实现,它仅支持从应用程序配置文件中读取配置信息。
  • 就像我在第一章的“提示”部分介绍的,客户程序必须总是很小心的关闭ChannelFactory以释放它占用的资源。

要克服这些限制,我们可以考虑对将ChannelFactory类包装成一个安全的WCF服务定位器。这样一个服务定位器的实现要注意下面几点:

  • 这个WCF服务定位器应该可以从其他地方如集中化的配置存储读取用于构造Binding的配置信息,而不是只能从应用程序配置文件中。
  • 这个WCF服务定位创建的ChannelFactory的资源释放应该以最佳实现包装在这个WCF服务定位器的IDsiposable接口和析构函数的实现中。客户程序原则上应该总是要记得在消费完服务以后显式的地调用WCF服务定位器的Dispose()方法。尽管,即使漏了显式调用,他的析构函数也应该能在实例被垃圾回收的时候调用Dispose()。
  • 这个WCF服务定位器不应该被全局缓存或实现为单例模式,因为构造和保持ChannelFactory的资源开销是很昂贵的,所以应该尽早在消费完服务以后释放。

下面是一个WCF服务定位器的示例:

  1     public sealed class WcfServiceLocator : IServiceLocator
  2     {
  3         #region Private Members
  4 
  5             //
  6 
  7         #endregion
  8 
  9         #region Public Methods
 10 
 11         /// <summary>
 12         /// Generic version of GetService
 13         /// </summary>
 14         /// <typeparam name="T">The service contract type</typeparam>
 15         /// <returns>The service proxy instance</returns>
 16         public T GetService<T>()
 17         {
 18             if (!_channelFactories.ContainsKey(typeof(T)))
 19             {
 20                 lock (_channelFactories)
 21                 {
 22                     if (!_channelFactories.ContainsKey(typeof(T)))
 23                     {
 24                         var cf = CreateChannelFactory<T>();
 25                         if (cf != null)
 26                         {
 27                             _channelFactories.Add(typeof(T), cf);
 28                             try
 29                             {
 30                                 _serviceProxies.Add(typeof(T), cf.CreateChannel());
 31                             }
 32                             catch
 33                             {
 34                                 _serviceProxies.Add(typeof(T), null);
 35                             }
 36                         }
 37                         else
 38                         {
 39                             _channelFactories.Add(typeof(T), null);
 40                             _serviceProxies.Add(typeof(T), null);
 41                         }
 42                     }
 43                 }
 44             }
 45 
 46             return (T)_serviceProxies[typeof(T)];
 47         }
 48 
 49         #endregion
 50 
 51         #region IDisposable Members
 52 
 53         /// <summary>
 54         /// The dispose method,
 55         /// closing all created channel factories in this service locator
 56         /// </summary>
 57         public void Dispose()
 58         {
 59             Dispose(true);
 60             GC.SuppressFinalize(this);
 61         }
 62 
 63         private bool disposed;
 64 
 65         private void Dispose(bool disposing)
 66         {
 67             if (disposed) return;
 68             if (disposing)
 69             {
 70                 foreach (var item in _channelFactories.Values)
 71                 {
 72                     if (item != null)
 73                     {
 74                         //close channel factory in best practice
 75                         //refer to: http://bloggingabout.net/blogs/erwyn/archive/2006/12/09/WCF-Service-Proxy-Helper.aspx
 76                         try
 77                         {
 78                             item.Close();
 79                         }
 80                         catch (CommunicationException)
 81                         {
 82                             item.Abort();
 83                         }
 84                         catch (TimeoutException)
 85                         {
 86                             item.Abort();
 87                         }
 88                         catch (Exception)
 89                         {
 90                             item.Abort();
 91                             throw;
 92                         }
 93                     }
 94                 }
 95             }
 96 
 97             disposed = true;
 98         }
 99 
100         ~WcfServiceLocator()
101         {
102             Dispose(false);
103         }
104 
105         #endregion
106     } 

整合第三方的IoC容器

如果所有的服务全都用WCF服务实现,那么上面这个WCF服务定位器就足够好了。但是,大多数程序同时会使用诸如辅助工具、日志记录、第三方组件包装等等本地服务。这样一来我们就同样需要诸如Unity和Castle IoC容器这样的本地服务定位器。所以,如果我们能够整合WCF服务定位器和第三方的服务定位器,那就更好了。

下面是一个示例的ServiceManager类,它拥有整合第三方服务定位器和上述WCF服务定位器的能力:

  1     public interface IServiceLocator : IDisposable
  2     {
  3         /// <summary>
  4         /// Get service
  5         /// </summary>
  6         /// <param name="serviceContract">The service contract type</param>
  7         /// <returns>The service instance</returns>
  8         object GetService(Type serviceContract);
  9 
 10         /// <summary>
 11         /// Generic version of GetService
 12         /// </summary>
 13         /// <typeparam name="T">The service contract type</typeparam>
 14         /// <returns>The service instance</returns>
 15         T GetService<T>();
 16     }
 17 
 18     public sealed class ServiceManager
 19     {
 20         #region Private Singleton
 21 
 22         private static readonly ServiceManager _singleton = new ServiceManager();
 23 
 24         #endregion
 25 
 26         #region Private Constructor
 27 
 28         private readonly Type _externalServiceLocatorType;
 29         private readonly List<Type> _cachedExternalServiceTypes = new List<Type>();
 30         private readonly List<Type> _cachedWcfServiceTypes = new List<Type>();
 31 
 32         private ServiceManager()
 33         {
 34             var serviceLocatorTypeName = ConfigurationManager.AppSettings[Constants.ExternalServiceLocatorTypeAppSettingName];
 35             if (!string.IsNullOrEmpty(serviceLocatorTypeName))
 36             {
 37                 var serviceLocatorType = Type.GetType(serviceLocatorTypeName);
 38                 if (serviceLocatorType != null)
 39                 {
 40                     if (serviceLocatorType != typeof(WcfServiceLocator))
 41                     {
 42                         _externalServiceLocatorType = serviceLocatorType;
 43                     }
 44                 }
 45             }
 46         }
 47 
 48         #endregion
 49 
 50         #region Public Methods
 51 
 52         /// <summary>
 53         /// Get service locator of specified service contract type
 54         /// </summary>
 55         /// <param name="serviceContract">The service contract type</param>
 56         /// <returns>The service instance</returns>
 57         public static IServiceLocator GetServiceLocator(Type serviceContract)
 58         {
 59             if (serviceContract == null)
 60                 throw new ArgumentNullException("serviceContract");
 61 
 62             if (_singleton._externalServiceLocatorType != null)
 63             {
 64                 if (!_singleton._cachedExternalServiceTypes.Contains(serviceContract))
 65                 {
 66                     if (_singleton._cachedWcfServiceTypes.Contains(serviceContract))
 67                     {
 68                         return new WcfServiceLocator();
 69                     }
 70                     lock (_singleton)
 71                     {
 72                         if (!_singleton._cachedExternalServiceTypes.Contains(serviceContract))
 73                         {
 74                             if (_singleton._cachedWcfServiceTypes.Contains(serviceContract))
 75                             {
 76                                 return new WcfServiceLocator();
 77                             }
 78 
 79                             //rethrow the exception in initializing the locator instance directly
 80                             var locator = (IServiceLocator)Activator.CreateInstance(_singleton._externalServiceLocatorType);
 81 
 82                             object service = null;
 83                             try
 84                             {
 85                                 service = locator.GetService(serviceContract);
 86                                 if (service != null)
 87                                 {
 88                                     _singleton._cachedExternalServiceTypes.Add(serviceContract);
 89                                     return locator;
 90                                 }
 91                             }
 92                             catch
 93                             {
 94                                 //if could not locate the service
 95                                 _singleton._cachedWcfServiceTypes.Add(serviceContract);
 96                                 return new WcfServiceLocator();
 97                             }
 98                             finally
 99                             {
100                                 if (service != null)
101                                 {
102                                     var disposable = service as IDisposable;
103                                     if (disposable != null)
104                                         disposable.Dispose();
105                                 }
106                             }
107                         }
108                     }
109                 }
110                 else
111                 {
112                     var locator = (IServiceLocator)Activator.CreateInstance(_singleton._externalServiceLocatorType);
113                     return locator;
114                 }
115             }
116 
117             _singleton._cachedWcfServiceTypes.Add(serviceContract);
118             return new
 WcfServiceLocator();
119         }
120 
121         /// <summary>
122         /// Get service locator of specified service contract type
123         /// </summary>
124         /// <typeparam name="T">The service contract type</typeparam>
125         /// <returns></returns>
126         public static IServiceLocator GetServiceLocator<T>()
127         {
128             return GetServiceLocator(typeof(T));
129         }
130 
131         #endregion
132     }

注意:

  • ServiceManager类的GetService()的返回类型应该要是IServiceLocator,而不是service本身的实例,因为对于WCF服务定位器来说,我们应该在消费完服务之后立即显式调用Dispose()方法来释放被占用的资源。
  • 这个ServiceManager类也可以用单例模式实现代替这里的静态类方式。
  • 注意ServiceManager类中的高亮代码,它为服务契约和服务定位器的映射的缓存实现了一个双重检测锁,确保其线程安全。
  • 提示

  • 要使用本文提到的WCF服务定位器或ServiceManager类,应该让服务的消费程序和提供程序都直接引用共同的定义了服务契约的DLL。不要让客户程序使用生成工具通过发布的元数据生成的代理类。
  •  

    参考

    (1) SOA Design Pattern Catalog: http://www.soapatterns.org/

     

    //我是结尾符,待续…

    posted @ 2009-03-26 23:57  Teddy's Knowledge Base  Views(2792)  Comments(1Edit  收藏