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