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