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