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 = ?
摘要
本文提供一种在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 }
注意:
提示
参考
(1) SOA Design Pattern Catalog: http://www.soapatterns.org/
//我是结尾符,待续…