#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