using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Implab {
///
/// Коллекция сервисов, позволяет регистрировать и получать сервисы.
///
public class ServiceLocator: Component, IServiceLocator, IServiceProvider {
// запись об сервисе
struct ServiceEntry {
public object service; // сервис
public bool shared; // признак того, что сервис НЕ нужно освобождать
public Func activator; // активатор сервиса при первом обращении
public List associated; // ссылки на текущую запись
public Type origin; // ссылка на оригинальную запись о сервисе
}
// словарь существующих сервисов
Dictionary m_services = new Dictionary();
///
/// Получает объект предоставляющий сервис .
///
/// Тип запрашиваемого сервиса
/// Объект, реализующий сервис
/// Сервис не зарегистрирован
public T GetService() {
return (T)GetService(typeof(T));
}
///
/// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис
/// не возникает исключений.
///
/// Тип требуемого сервиса.
/// Объект реализующий сервис, или default(T) если такового нет.
/// true - сервис найден, false - сервис не зарегистрирован.
public bool TryGetService(out T service) {
AssertNotDisposed();
var result = GetService(typeof(T), false);
if (result == null) {
service = default(T);
return false;
} else {
service = (T)result;
return true;
}
}
///
/// Получает объект предоставляющий сервис
///
/// Тип запрашиваемого сервиса
/// Объект, реализующий сервис
/// Сервис не зарегистрирован
public object GetService(Type serviceType) {
return GetService (serviceType, true);
}
public virtual object GetService(Type serviceType, bool throwOnError) {
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();
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;
}
if (throwOnError)
throw new Exception("Unable to create a service instance");
return null;
}
///
/// Регистрирует фабрику для активации сервиса по первому требованию.
///
/// Тип регистрируемого сервиса.
/// Фабрика для создания/получения объекта, предоставляющего сервис.
/// Указанный сервис уже зарегистрирован.
/// При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.
public void Register(Func activator) {
if (activator == null)
throw new ArgumentNullException("activator");
AssertNotDisposed();
Unregister(typeof(T));
m_services[typeof(T)] = new ServiceEntry {
activator = () => activator() as object
};
}
///
/// Регистрирует объект, предоставляющий сервис.
///
/// Тип регистрируемого сервиса.
/// Объект, предоставляющий сервис.
/// Указанный сервис уже зарегистрирован.
/// Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.
public void Register(T service) {
Register(service, true);
}
///
/// Регистрирует объект, предоставляющий сервис.
///
/// Тип регистрируемого сервиса.
/// Объект, предоставляющий сервис.
/// Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.
/// Указанный сервис уже зарегистрирован.
public void Register(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);
}
}
///
/// Освобождает зарегистрированные сервисы (которые требуется освобоить).
///
/// Призанак того, что нужно освободить ресурсы.
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);
}
}
}