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