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