|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
using System.Web;
|
|
|
|
|
|
namespace Implab {
|
|
|
/// <summary>
|
|
|
/// Коллекция сервисов, позволяет регистрировать и получать сервисы.
|
|
|
/// </summary>
|
|
|
public class ServiceLocator: Component, IServiceLocator, IServiceProvider {
|
|
|
// запись об сервисе
|
|
|
struct ServiceEntry {
|
|
|
public object service; // сервис
|
|
|
public bool shared; // признак того, что сервис НЕ нужно освобождать
|
|
|
public Func<object> activator; // активатор сервиса при первом обращении
|
|
|
public List<Type> associated; // ссылки на текущую запись
|
|
|
public Type origin; // ссылка на оригинальную запись о сервисе
|
|
|
}
|
|
|
|
|
|
// словарь существующих сервисов
|
|
|
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>() {
|
|
|
return (T)GetService(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) {
|
|
|
AssertNotDisposed();
|
|
|
|
|
|
try {
|
|
|
service = GetService<T>();
|
|
|
return true;
|
|
|
} catch(KeyNotFoundException) {
|
|
|
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) {
|
|
|
if (serviceType == null)
|
|
|
throw new ArgumentNullException("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)
|
|
|
throw new ApplicationException(String.Format("{0} doesn't provide {1} service",this,serviceType));
|
|
|
|
|
|
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)
|
|
|
return se.service;
|
|
|
|
|
|
// текущая запись является ссылкой
|
|
|
if (se.origin != null) {
|
|
|
se.service = GetService(se.origin);
|
|
|
m_services[serviceType] = se;
|
|
|
return se.service;
|
|
|
}
|
|
|
|
|
|
// текущая запись не является ссылкой и не имеет информации о сервисе
|
|
|
// она должна сожержать информацию об активации
|
|
|
if (se.activator != null) {
|
|
|
se.service = se.activator();
|
|
|
|
|
|
m_services[serviceType] = se;
|
|
|
|
|
|
return se.service;
|
|
|
}
|
|
|
|
|
|
throw new Exception("Unable to create a service instance");
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Регистрирует фабрику для активации сервиса по первому требованию.
|
|
|
/// </summary>
|
|
|
/// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
|
|
|
/// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param>
|
|
|
/// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
|
|
|
/// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks>
|
|
|
public void Register<T>(Func<T> activator) {
|
|
|
if (activator == null)
|
|
|
throw new ArgumentNullException("activator");
|
|
|
|
|
|
AssertNotDisposed();
|
|
|
|
|
|
Unregister(typeof(T));
|
|
|
|
|
|
m_services[typeof(T)] = new ServiceEntry {
|
|
|
activator = () => activator() as object
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/// <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>
|
|
|
/// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
|
|
|
public void Register<T>(T service, bool shared) {
|
|
|
if (service == null)
|
|
|
throw new ArgumentNullException("service");
|
|
|
|
|
|
AssertNotDisposed();
|
|
|
|
|
|
Unregister(typeof(T));
|
|
|
|
|
|
m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared };
|
|
|
}
|
|
|
|
|
|
public void Unregister(Type serviceType) {
|
|
|
if (serviceType == null)
|
|
|
throw new ArgumentNullException("serviceType");
|
|
|
|
|
|
AssertNotDisposed();
|
|
|
|
|
|
ServiceEntry se;
|
|
|
if (m_services.TryGetValue(serviceType, out se)) {
|
|
|
// освобождаем ресурсы
|
|
|
if (se.service != null && !se.shared)
|
|
|
((IDisposable)se.service).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)
|
|
|
if (!entry.shared && entry.service is IDisposable)
|
|
|
((IDisposable)entry.service).Dispose();
|
|
|
|
|
|
}
|
|
|
base.Dispose(disposing);
|
|
|
}
|
|
|
}
|
|
|
}
|