#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; using Nuclex.Support.Plugins; #if ENABLE_SERVICEMANAGER namespace Nuclex.Support.Services { // Allow Dependency on Container // public Foo(IServiceProvider serviceProvider) // public Foo(IserviceLocator serviceLocator) // public Foo(Container container) /// /// Inversion of Control container that manages the services of an application /// /// /// /// This is a very lightweight and simple inversion of control container that /// relieves components of their duty to track down implementations for the services /// they require to function. It will help with lazy initialization and prevent /// components from becoming cross-linked balls of spaghetti references. /// /// /// Here's a short list of the terms used throughout this container and their /// specific meaning in this context. /// /// /// /// /// Service /// /// Defined by an interface (service contract) and provided by a component /// that implements the service contract. A service provides some kind of /// utility to the application, for example it could provide access to /// a data base or allow other components to control certain aspects of /// the application. /// /// /// /// Contract /// /// Interface defining the behavior that a service implementation has to /// follow. In order for a component to become a suitable candidate for /// providing a specific service, it has to implement the service contract /// interface and should rigorously follow its specifications. /// /// /// /// Component /// /// A component is simply a class that implements one or more service /// contracts. The service manager will created instances of these classes /// when all their dependencies can be provided for and an implementation /// of their service contract is requested. /// /// /// /// /// public partial class ServiceManager : IServiceProvider { #region class Contract /// Stores the settings for an individual contract private class Contract { /// /// Factory by which instances of the contract implementation can be created /// public IAbstractFactory Factory; /// How instances of the implementation are to be managed public Instancing Instancing; /// Single global instance of the contract implementation /// /// Used only if is set to Singleton /// public object SingletonInstance; /// Thread-local instance of the contract implementation /// /// Used only if is set to InstancePerThread /// public object ThreadLocalInstance { get { initializeThreadLocalData(); return Thread.GetData(this.threadLocalDataSlot); } set { initializeThreadLocalData(); Thread.SetData(this.threadLocalDataSlot, value); } } /// Initializes the thread-local data slot private void initializeThreadLocalData() { if(this.threadLocalDataSlot == null) { lock(this) { if(this.threadLocalDataSlot == null) { this.threadLocalDataSlot = Thread.AllocateDataSlot(); } } } } /// Arguments to be passed to the component constructor private Dictionary arguments; /// Data slot for thread local storage /// /// We're using an explicit data slot because the ThreadStaticAttribute class /// can only be used on static fields and also because this class is not /// supported by the .NET Compact Framework. /// private volatile LocalDataStoreSlot threadLocalDataSlot; } #endregion // class Contract #if WINDOWS /// Initializes a new service manager /// /// This overload will automatically use a type lister that causes all types /// in all loaded assemblies of the calling app domain to be considered /// by the service manager for obtaining contract implementations. /// public ServiceManager() : this(new AppDomainTypeLister()) { } #endif // WINDOWS /// Initializes a new service manager /// /// Type lister providing the types considered by the service manager for /// obtaining contract implementations. /// public ServiceManager(ITypeLister typeLister) { this.typeLister = typeLister; this.contracts = new Dictionary(); } /// /// Returns all available implementations for the specified contract /// /// /// A new enumerator for the available contract implementations /// public IEnumerable GetComponents() where ContractType : class { Type contractType = typeof(ContractType); foreach(Type checkedType in this.typeLister.GetTypes()) { bool isImplementationOfContract = (!checkedType.IsAbstract) && contractType.IsAssignableFrom(checkedType); if(isImplementationOfContract) { yield return checkedType; } } } /// /// Returns all available implementations for the specified contract /// /// /// If true, only services whose dependencies can be completely /// satisfied by the container are returned. /// /// /// A new enumerator for the available contract implementations /// public IEnumerable GetComponents(bool completeOnly) where ContractType : class { if(completeOnly) { return filterCompleteComponents(GetComponents()); } else { return GetComponents(); } } /// /// Filters a list of components so only components whose dependencies can be /// completely provided are enumerated /// /// Enumerable type list that will be filtered /// /// Only those components whose dependencies can be completely provided /// private IEnumerable filterCompleteComponents(IEnumerable types) { foreach(Type type in types) { bool isCandidate = (!type.IsValueType) && (!type.IsAbstract) && (type.IsPublic || type.IsNestedPublic); if(isCandidate) { ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public); // If a contract has been Contract contract; if(this.contracts.TryGetValue(type, out contract)) { yield return type; } else { } yield return type; } } } /// /// Allows the adjustment of the container's behavior in regard to /// the specified contract /// /// /// Contract for which the behavior will be adjusted /// /// /// A context object through which the behavior of the container can be /// adjusted for the specified type /// public ForContext For() where ContractType : class { return new ForContext(this); } /// /// Allows the adjustment of the container's behavior in regard to /// the specified contract /// /// /// Contract for which the behavior will be adjusted /// /// /// A context object through which the behavior of the container can be /// adjusted for the specified type /// public ForContext For(Type contractType) { return new ForContext(this, contractType); } /// Retrieves the service of the specified type /// /// Contract for which the service will be retrieved /// /// The service for the specified contract public ContractType GetService() where ContractType : class { return (ContractType)GetService(typeof(ContractType)); } /// Retrieves the service of the specified type /// /// Contract for which the service will be retrieved /// /// The service for the specified contract public object GetService(Type contractType) { Contract c = resolveContract(contractType); return c.Factory.CreateInstance(); // TODO: Honor the contract settings } /// /// Resolves all dependencies required to create a service for a contract /// /// /// Type of contract for which to resolve the implementation /// /// The settings for the contract including a valid factory private Contract resolveContract(Type contractType) { if(contractType.IsValueType) { throw new ArgumentException( "Contracts have to be interfaces or classes", "contractType" ); } /* Contract contract; if(this.contracts.TryGetValue(contractType, out contract)) { return contract; } */ throw new NotImplementedException(); } /// Lists all types partaking in the dependency injection private ITypeLister typeLister; /// Dictionary with settings for each individual contract private Dictionary contracts; } } // namespace Nuclex.Support.Services #endif // ENABLE_SERVICEMANAGER