using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Wayne.Lib { class ServiceContainer : IServiceContainer { private readonly IServiceLocator parentServiceLocator; private readonly Dictionary services; private bool disposed; private List> resolvers = new List>(); public ServiceContainer() : this(null) { } public ServiceContainer(IServiceLocator parentServiceLocator) { services = new Dictionary(); this.parentServiceLocator = parentServiceLocator; } public void RegisterResolver(Func requestedType) { resolvers.Add(requestedType); } public void RegisterService() { AssertContainerIsNotDisposed(); var instance = (TServiceImplementation)TryCreate(typeof(TServiceImplementation)); RegisterService(instance); } public void RegisterService(string serviceId) { AssertContainerIsNotDisposed(); var instance = (TServiceImplementation)TryCreate(typeof(TServiceImplementation), serviceId); RegisterService(instance, serviceId); } public void RegisterService(TServiceContract serviceInstance) { RegisterService(serviceInstance, string.Empty); } public void RegisterService(TServiceContract serviceInstance, string serviceId) { AssertContainerIsNotDisposed(); services[new ServiceKey(typeof(TServiceContract), serviceId)] = serviceInstance; } public void RegisterService(ObjectConstructor constructorMethod) { AssertContainerIsNotDisposed(); var serviceInstance = constructorMethod.Invoke(this); services[new ServiceKey(typeof(TServiceContract))] = serviceInstance; } public void RegisterService(ObjectConstructor constructorMethod, string serviceId) { AssertContainerIsNotDisposed(); var serviceInstance = constructorMethod.Invoke(this); services[new ServiceKey(typeof(TServiceContract), serviceId)] = serviceInstance; } public void RegisterService() where TServiceImplementation : TServiceContract where TServiceContract : class { AssertContainerIsNotDisposed(); var instance = (TServiceImplementation)TryCreate(typeof(TServiceImplementation)); if ((object)instance == null) //Cast to object to get rid of warning throw new ServiceContainerException(typeof(TServiceImplementation), "Could not create implementation"); RegisterService(instance); } public void RegisterService(string serviceId) where TServiceImplementation : TServiceContract where TServiceContract : class { AssertContainerIsNotDisposed(); var instance = (TServiceImplementation)TryCreate(typeof(TServiceImplementation), serviceId); if ((object)instance == null) //Cast to object to get rid of warning throw new ServiceContainerException(typeof(TServiceImplementation), "Could not create implementation"); RegisterService(instance, serviceId); } public TServiceContract GetService() { return (TServiceContract)GetService(typeof(TServiceContract)); } public TServiceContract GetService(string serviceId) { return (TServiceContract)GetService(typeof(TServiceContract), serviceId); } public object GetService(Type serviceType) { return GetService(serviceType, string.Empty); } public object GetService(Type serviceType, string serviceId) { AssertContainerIsNotDisposed(); object service; if (services.TryGetValue(new ServiceKey(serviceType, serviceId), out service)) return service; //Try resolvers service = resolvers .Select(x => x(serviceType)) .FirstOrDefault(x => x != null); if (service != null) return service; //Try parent container if (parentServiceLocator != null) return parentServiceLocator.GetService(serviceType, serviceId); throw ServiceContainerException.CreateNotFoundException(serviceType); } public TServiceContract GetServiceOrDefault(CreateDefaultService func, string serviceId) { AssertContainerIsNotDisposed(); object service; Type serviceType = typeof(TServiceContract); var key = new ServiceKey(serviceType, serviceId); if (!services.TryGetValue(key, out service)) { //Try resolvers service = resolvers .Select(x => x(serviceType)) .FirstOrDefault(x => x != null); if (service != null) { return (TServiceContract)service; } if (parentServiceLocator != null) return parentServiceLocator.GetServiceOrDefault(func, serviceId); return func(); } return (TServiceContract)service; } public TServiceContract GetServiceOrDefault(CreateDefaultService func) { return GetServiceOrDefault(func, string.Empty); } public T CreateInstance(params object[] additionalObjects) where T : class { return (T)CreateInstance(typeof(T), additionalObjects); } public object CreateInstance(Type typeToInstantiate, params object[] additionalParameter) { AssertContainerIsNotDisposed(); object result = TryCreate(typeToInstantiate, additionalParameter); if (result == null) throw new ServiceContainerException(typeToInstantiate, "Could not create object"); return result; } public T CreateInstance(string serviceId, params object[] additionalObjects) where T : class { return (T)CreateInstance(typeof(T), serviceId, additionalObjects); } public object CreateInstance(Type typeToInstantiate, string serviceId, params object[] additionalParameter) { AssertContainerIsNotDisposed(); object result = TryCreate(typeToInstantiate, serviceId, additionalParameter); if (result == null) throw new ServiceContainerException(typeToInstantiate, "Could not create object"); return result; } private void AssertContainerIsNotDisposed() { if (disposed) throw new ObjectDisposedException(GetType().Name); } public object TryGetService(Type serviceType) { return TryGetService(serviceType, string.Empty); } public object TryGetService(Type serviceType, string serviceId) { object service; if (serviceType == typeof(IServiceLocator) || serviceType == typeof(IServiceContainer)) return this; if (!services.TryGetValue(new ServiceKey(serviceType, serviceId), out service)) { //Try resolvers service = resolvers .Select(x => x(serviceType)) .FirstOrDefault(x => x != null); if (service != null) { return service; } if (parentServiceLocator != null) return parentServiceLocator.TryGetService(serviceType, serviceId); return null; } return service; } public T TryGetService() { return TryGetService(string.Empty); } public T TryGetService(string serviceId) { Type serviceType = typeof(T); object result; if (serviceType == typeof(IServiceLocator) || serviceType == typeof(IServiceContainer)) result = this; else { if (!services.TryGetValue(new ServiceKey(serviceType, serviceId), out result)) { //Try resolvers result= resolvers .Select(x => x(serviceType)) .FirstOrDefault(x => x != null); if (result != null) { return (T) result; } if (parentServiceLocator != null) result = parentServiceLocator.TryGetService(serviceType, serviceId); } } return (T)result; } private object TryCreate(Type typeToCreate, params object[] additionalObjects) { return TryCreate(typeToCreate, string.Empty, additionalObjects); } private object TryCreate(Type typeToCreate, string serviceId, params object[] additionalObjects) { var constructors = typeToCreate.GetConstructors(); Array.Reverse(constructors); List parameters = new List(); ConstructorInfo foundConstructor = null; foreach (ConstructorInfo constructor in constructors) { var parameterInfos = constructor.GetParameters(); parameters.Clear(); foreach (var parameterInfo in parameterInfos) { var dependency = TryGetService(parameterInfo.ParameterType, serviceId); if (dependency != null) { parameters.Add(dependency); } else { int firstMatchingParameterObjectIndex = -1; for (int index = 0; index < additionalObjects.Length; index++) { object additionalObject = additionalObjects[index]; if (parameterInfo.ParameterType.IsAssignableFrom(additionalObject.GetType())) { firstMatchingParameterObjectIndex = index; break; } } if (firstMatchingParameterObjectIndex >= 0) { parameters.Add(additionalObjects[firstMatchingParameterObjectIndex]); } } } if (parameters.Count == parameterInfos.Length) { foundConstructor = constructor; break; } } if (foundConstructor != null) { var stateObject = foundConstructor.Invoke(parameters.ToArray()); return stateObject; } return null; } // Help struct used as key in the dictionary of the container private struct ServiceKey { private readonly Type _typeOfObject; private readonly string _id; public ServiceKey(Type typeOfObject, string id) { _typeOfObject = typeOfObject; _id = id; } public ServiceKey(Type typeOfObject) { _typeOfObject = typeOfObject; _id = string.Empty; } public override int GetHashCode() { int hash = 17; hash = hash * 31 + _typeOfObject.GetHashCode(); hash = hash * 31 + _id.GetHashCode(); return hash; } public override bool Equals(object obj) { return obj is ServiceKey && this == (ServiceKey)obj; } public static bool operator ==(ServiceKey s1, ServiceKey s2) { return (s1._typeOfObject == s2._typeOfObject) && (s1._id.Equals(s2._id, StringComparison.InvariantCulture)); } public static bool operator !=(ServiceKey s1, ServiceKey s2) { return !(s1 == s2); } } #region Implementation of IDisposable ~ServiceContainer() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposed) return; disposed = true; if (disposing) { foreach (var service in services.Values) { var disposable = service as IDisposable; if (disposable != null) { try { disposable.Dispose(); } catch { //Ignore errors } } } services.Clear(); } } #endregion } }