Mercurial > pub > ImplabNet
diff Implab/Components/ServiceLocator.cs @ 152:240aa6994018 v2
component model refactoring
author | cin |
---|---|
date | Thu, 11 Feb 2016 01:56:27 +0300 |
parents | |
children | bdfdba6b645b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/ServiceLocator.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; + +namespace Implab.Components { + /// <summary> + /// Коллекция сервисов, позволяет регистрировать и получать сервисы. + /// </summary> + public class ServiceLocator: Disposable, IServiceLocator, IServiceProvider { + // запись о сервисе + struct ServiceEntry : IDisposable { + public object service; // сервис + public bool shared; // признак того, что сервис НЕ нужно освобождать + public Func<object> activator; // активатор сервиса при первом обращении + public Action<object> cleanup; // функция для очистки сервиса + public List<Type> associated; // ссылки на текущую запись + public Type origin; // ссылка на оригинальную запись о сервисе + + #region IDisposable implementation + + public void Dispose() { + if (shared) + return; + if (cleanup != null) { + if (service != null) + cleanup(service); + } else + Safe.Dispose(service); + } + + #endregion + } + + // словарь существующих сервисов + readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>(); + + /// <summary> + /// Получает объект предоставляющий сервис <typeparamref name="T"/>. + /// </summary> + /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam> + /// <returns>Объект, реализующий сервис</returns> + /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> + public T GetService<T>() { + object result; + if (TryGetService(typeof(T), out result)) + return (T)result; + throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T))); + } + + + /// <summary> + /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис + /// не возникает исключений. + /// </summary> + /// <typeparam name="T">Тип требуемого сервиса.</typeparam> + /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param> + /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns> + public bool TryGetService<T>(out T service) { + object result; + if (TryGetService(typeof(T), out result)) { + service = (T)result; + return true; + } + service = default(T); + return false; + } + + /// <summary> + /// Получает объект предоставляющий сервис <paramref name="serviceType"/> + /// </summary> + /// <param name="serviceType">Тип запрашиваемого сервиса</param> + /// <returns>Объект, реализующий сервис</returns> + /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> + public object GetService(Type serviceType) { + object result; + if (TryGetService(serviceType, out result)) + return result; + throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType)); + } + + /// <summary> + /// Пытается получить требуемый сервис или совместимый с ним. + /// </summary> + /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns> + /// <param name="serviceType">Тип запрашиваемого сервиса.</param> + /// <param name="service">Искомый сервис.</param> + public virtual bool TryGetService(Type serviceType, out object service) { + Safe.ArgumentNotNull(serviceType, "serviceType"); + AssertNotDisposed(); + + ServiceEntry se; + if (!m_services.TryGetValue(serviceType, out se)) { + // ищем ближайщий объект, реализующий нужный сервис + Type pt = null; + foreach (var t in m_services.Keys) + if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt))) + pt = t; + + if (pt == null) { + // нет нужного сервиса + service = null; + return false; + } + + var pe = m_services[pt]; + + // найденная запись может ссылаться на оригинальную запись с сервисом + if(pe.origin != null) { + pt = pe.origin; + pe = m_services[pt]; + } + + // добавляем список с обратными ссылками + if (pe.associated == null) + pe.associated = new List<Type>(); + + pe.associated.Add(serviceType); + + // обновляем родительскую запись + m_services[pt] = pe; + + // создаем запись со ссылкой + se = new ServiceEntry { + service = pe.service, + origin = pt, + shared = true // предотвращаем множественные попытки освобождения + }; + + m_services[serviceType] = se; + } + + // запись содержит в себе информацию о сервисе + if (se.service != null) { + service = se.service; + return true; + } + + // текущая запись является ссылкой + if (se.origin != null) { + se.service = GetService(se.origin); + m_services[serviceType] = se; + service = se.service; + return true; + } + + // текущая запись не является ссылкой и не имеет информации о сервисе + // она должна сожержать информацию об активации + if (se.activator != null) { + se.service = se.activator(); + + m_services[serviceType] = se; + + service = se.service; + return true; + } + + service = null; + return false; + } + + /// <summary> + /// Регистрирует фабрику для активации сервиса по первому требованию. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param> + /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param> + /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks> + public void Register<T>(Func<T> activator, Action<T> cleanup) { + Safe.ArgumentNotNull(activator, "activator"); + + AssertNotDisposed(); + + Unregister(typeof(T)); + + var serviceEntry = new ServiceEntry(); + serviceEntry.activator = () => activator(); + if (cleanup != null) + serviceEntry.cleanup = instance => cleanup((T)instance); + m_services[typeof(T)] = serviceEntry; + } + + public void Register<T>(Func<T> activator) { + Register(activator, null); + } + + /// <summary> + /// Регистрирует объект, предоставляющий сервис. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="service">Объект, предоставляющий сервис.</param> + /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception> + /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks> + public void Register<T>(T service) { + Register(service, true); + } + + /// <summary> + /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="service">Объект, предоставляющий сервис.</param> + /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param> + public void Register<T>(T service, bool shared) { + Safe.ArgumentNotNull(service, "service"); + + AssertNotDisposed(); + + Unregister(typeof(T)); + + m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared }; + } + + public void Unregister(Type serviceType) { + Safe.ArgumentNotNull(serviceType, "serviceType"); + + AssertNotDisposed(); + + ServiceEntry se; + if (m_services.TryGetValue(serviceType, out se)) { + if (se.origin != null) { + var pe = m_services[se.origin]; + pe.associated.Remove(serviceType); + } + // освобождаем ресурсы + se.Dispose(); + m_services.Remove(serviceType); + + // убираем связанные записи + if (se.associated != null) + foreach (var item in se.associated) + m_services.Remove(item); + } + } + + /// <summary> + /// Освобождает зарегистрированные сервисы (которые требуется освобоить). + /// </summary> + /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param> + protected override void Dispose(bool disposing) { + if (disposing) { + + foreach (var entry in m_services.Values) + entry.Dispose(); + + } + base.Dispose(disposing); + } + } +} \ No newline at end of file