comparison Implab/ServiceLocator.cs @ 40:fe33f4e02ad5

improved tracing added text listeners (file,console)
author cin
date Tue, 15 Apr 2014 17:52:09 +0400
parents
children 9dd6a896a385
comparison
equal deleted inserted replaced
39:6498078ae368 40:fe33f4e02ad5
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 }