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