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