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/
//我是结尾符,待续…