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