##// END OF EJS Templates
ServiceLocator: small refactoring, GetService method is made virtual
cin -
r68:9dd6a896a385 default
parent child
Show More
@@ -1,209 +1,216
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Web;
5 5
6 6 namespace Implab {
7 7 /// <summary>
8 8 /// Коллекция сервисов, позволяет регистрировать и получать сервисы.
9 9 /// </summary>
10 10 public class ServiceLocator: Component, IServiceLocator, IServiceProvider {
11 11 // запись об сервисе
12 12 struct ServiceEntry {
13 13 public object service; // сервис
14 14 public bool shared; // признак того, что сервис НЕ нужно освобождать
15 15 public Func<object> activator; // активатор сервиса при первом обращении
16 16 public List<Type> associated; // ссылки на текущую запись
17 17 public Type origin; // ссылка на оригинальную запись о сервисе
18 18 }
19 19
20 20 // словарь существующих сервисов
21 21 Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>();
22 22
23 23 /// <summary>
24 24 /// Получает объект предоставляющий сервис <typeparamref name="T"/>.
25 25 /// </summary>
26 26 /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam>
27 27 /// <returns>Объект, реализующий сервис</returns>
28 28 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
29 29 public T GetService<T>() {
30 30 return (T)GetService(typeof(T));
31 31 }
32 32
33 33
34 34 /// <summary>
35 35 /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис
36 36 /// не возникает исключений.
37 37 /// </summary>
38 38 /// <typeparam name="T">Тип требуемого сервиса.</typeparam>
39 39 /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param>
40 40 /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns>
41 41 public bool TryGetService<T>(out T service) {
42 42 AssertNotDisposed();
43 43
44 try {
45 service = GetService<T>();
46 return true;
47 } catch(KeyNotFoundException) {
48 service = default(T);
49 return false;
50 }
44 var result = GetService(typeof(T), false);
45 if (result == null) {
46 service = default(T);
47 return false;
48 } else {
49 service = (T)result;
50 return true;
51 }
51 52 }
52 53
53 54 /// <summary>
54 55 /// Получает объект предоставляющий сервис <paramref name="serviceType"/>
55 56 /// </summary>
56 57 /// <param name="serviceType">Тип запрашиваемого сервиса</param>
57 58 /// <returns>Объект, реализующий сервис</returns>
58 59 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
59 public object GetService(Type serviceType) {
60 public object GetService(Type serviceType) {
61 return GetService (serviceType, true);
62 }
63
64 public virtual object GetService(Type serviceType, bool throwOnError) {
60 65 if (serviceType == null)
61 66 throw new ArgumentNullException("serviceType");
62 67 AssertNotDisposed();
63 68
64 69 ServiceEntry se;
65 70 if (!m_services.TryGetValue(serviceType, out se)) {
66 71 // ищем ближайщий объект, реализующий нужный сервис
67 72 Type pt = null;
68 73 foreach (var t in m_services.Keys)
69 74 if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt)))
70 75 pt = t;
71 76 if (pt == null)
72 77 throw new ApplicationException(String.Format("{0} doesn't provide {1} service",this,serviceType));
73 78
74 79 var pe = m_services[pt];
75 80
76 81 // найденная запись может ссылаться на оригинальную запись с сервисом
77 82 if(pe.origin != null) {
78 83 pt = pe.origin;
79 84 pe = m_services[pt];
80 85 }
81 86
82 87 // добавляем список с обратными ссылками
83 88 if (pe.associated == null)
84 89 pe.associated = new List<Type>();
85 90
86 91 pe.associated.Add(serviceType);
87 92
88 93 // обновляем родительскую запись
89 94 m_services[pt] = pe;
90 95
91 96 // создаем запись со ссылкой
92 97 se = new ServiceEntry {
93 98 service = pe.service,
94 99 origin = pt,
95 100 shared = true // предотвращаем множественные попытки освобождения
96 101 };
97 102
98 103 m_services[serviceType] = se;
99 104 }
100 105
101 106 // запись содержит в себе информацию о сервисе
102 107 if (se.service != null)
103 108 return se.service;
104 109
105 110 // текущая запись является ссылкой
106 111 if (se.origin != null) {
107 112 se.service = GetService(se.origin);
108 113 m_services[serviceType] = se;
109 114 return se.service;
110 115 }
111 116
112 117 // текущая запись не является ссылкой и не имеет информации о сервисе
113 118 // она должна сожержать информацию об активации
114 119 if (se.activator != null) {
115 120 se.service = se.activator();
116 121
117 122 m_services[serviceType] = se;
118 123
119 124 return se.service;
120 125 }
121 126
122 throw new Exception("Unable to create a service instance");
127 if (throwOnError)
128 throw new Exception("Unable to create a service instance");
129 return null;
123 130 }
124 131
125 132 /// <summary>
126 133 /// Регистрирует фабрику для активации сервиса по первому требованию.
127 134 /// </summary>
128 135 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
129 136 /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param>
130 137 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
131 138 /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks>
132 139 public void Register<T>(Func<T> activator) {
133 140 if (activator == null)
134 141 throw new ArgumentNullException("activator");
135 142
136 143 AssertNotDisposed();
137 144
138 145 Unregister(typeof(T));
139 146
140 147 m_services[typeof(T)] = new ServiceEntry {
141 148 activator = () => activator() as object
142 149 };
143 150 }
144 151
145 152 /// <summary>
146 153 /// Регистрирует объект, предоставляющий сервис.
147 154 /// </summary>
148 155 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
149 156 /// <param name="service">Объект, предоставляющий сервис.</param>
150 157 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
151 158 /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks>
152 159 public void Register<T>(T service) {
153 160 Register(service, true);
154 161 }
155 162
156 163 /// <summary>
157 164 /// Регистрирует объект, предоставляющий сервис.
158 165 /// </summary>
159 166 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
160 167 /// <param name="service">Объект, предоставляющий сервис.</param>
161 168 /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param>
162 169 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
163 170 public void Register<T>(T service, bool shared) {
164 171 if (service == null)
165 172 throw new ArgumentNullException("service");
166 173
167 174 AssertNotDisposed();
168 175
169 176 Unregister(typeof(T));
170 177
171 178 m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared };
172 179 }
173 180
174 181 public void Unregister(Type serviceType) {
175 182 if (serviceType == null)
176 183 throw new ArgumentNullException("serviceType");
177 184
178 185 AssertNotDisposed();
179 186
180 187 ServiceEntry se;
181 188 if (m_services.TryGetValue(serviceType, out se)) {
182 189 // освобождаем ресурсы
183 190 if (se.service != null && !se.shared)
184 191 ((IDisposable)se.service).Dispose();
185 192 m_services.Remove(serviceType);
186 193
187 194 // убираем связанные записи
188 195 if (se.associated != null)
189 196 foreach (var item in se.associated)
190 197 m_services.Remove(item);
191 198 }
192 199 }
193 200
194 201 /// <summary>
195 202 /// Освобождает зарегистрированные сервисы (которые требуется освобоить).
196 203 /// </summary>
197 204 /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param>
198 205 protected override void Dispose(bool disposing) {
199 206 if (disposing) {
200 207
201 208 foreach (var entry in m_services.Values)
202 209 if (!entry.shared && entry.service is IDisposable)
203 210 ((IDisposable)entry.service).Dispose();
204 211
205 212 }
206 213 base.Dispose(disposing);
207 214 }
208 215 }
209 216 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now