Skip to content
grumpydev edited this page Jan 26, 2011 · 5 revisions

Welcome to TinyIoC / TinyMessenger

Overview

Welcome to TinyIoC - an easy to use, hassle free, Inversion of Control Container. TinyIoC has been designed to fulfil a single key requirement - to lower the "level of entry" for using an IoC container; both for small projects, and developers who are new to IoC who might be "scared" of the "big boys"!

To that end, TinyIoC attempts to stick to the following core principals:

  • Simplfied Inclusion - No assembly to reference, no binary to worry about, just a single cs file you can include in your project and you're good to go. It even works with both Mono and MonoTouch for iPhone development!
  • Simplified Setup - With auto-resolving of concrete types and an "auto registration" option for interfaces setup is a piece of cake. It can be reduced to 0 lines for concrete types, or 1 line if you have any interface dependencies!
  • Simple, "Fluent" API - Just because it's "Tiny", doesn't mean it has no features. A simple "fluent" API gives you access to the more advanced features, like specifying singleton/multi-instance, strong or weak references or forcing a particular constructor.

In addition to this, TinyIoC's "simplified inclusion" makes it useful for providing DI for internal library classes, or providing your library the ability to use DI without the consuming developer having to specify a container (although it's useful to provide the option to do so).

Note For ASP.Net per-request lifetime support you will need to also include TinyIoCAspNetExtensions.cs, and the TinyIoC namespace. This provides an extension method for supporting per-request registrations. It's an extra file, but it's preferable to taking a dependency on Asp.Net in the main file, which then requires users to setup #DEFINEs for non-asp.net platforms.

Key Features

  • Simple inclusion - just add the CS file (or VB file coming soon!) and off you go.
  • Wide platform support - actively tested on Windows, Mono, MonoTouch, PocketPC and Windows Phone 7. Also works just fine on MonoDroid.
  • Simple API for Register, Resolve, CanResolve and TryResolve.
  • Supports constructor injection and property injection. Constructors are selected automatically but can be overridden using a "fluent" API.
  • Lifetime management - including singletons, multi-instance and ASP.Net per-request singletons.
  • Automatic lazy factories - a Func dependency will automatically create a factory.
  • RegisterMultiple/ResolveAll/IEnumerable support - multiple implementations of an interface can be registered and resolved to an IEnumerable using ResolveAll, or taking a dependency on IEnumerable.
  • Child containers - lifetime can be managed using child containers, with automatic "bubbling" of resolving to parent containers where required.

TinyMessenger

As well as the base container, the TinyIoC project also includes an Event Aggregation/Messenger system called TinyMessenger. TinyMessenger allows for publishing of and subscribing to "messages" in a loosely coupled manner. TinyMessengeris independent of TinyIoC but if you uncomment line 18 (#define TINYMESSENGER) in TinyIoC.cs it will automatically register TinyMessenger in your root container) but not in any child containers by default. For more information see the TinyMessenger page.

License

TinyIoC is released under the Ms-PL license - which /should/ mean (and I'm certainly no lawyer) you can include it in commercial and non-commercial software, but you can't take it and re-release it under an incompatible licence such as GPL. More information can be found on the licensing page. If you feel this licence doesn't fit your requirements feel free to get in touch.

Key Feature Examples

Simple Setup

// TinyIoC provides a lazyily constructed singleton
// version of itself if you want to use it.
var container = TinyIoCContainer.Current;

// By default we can resolve concrete types without
// registration
var instance = container.Resolve<MyConcreteType>();

// We can automatically register all concrete types, abstract base classes
// and interfaces with a single call.
container.AutoRegister();
var implementation = container.Resolve<IMyInterface>();

// By default we silently ignore duplicate implementations,
// but we can choose to throw a meaningful exception that contains
// the interface/base type and all the potential implementations:
container.AutoRegister(false); 

Registration API

// We support name and unnamed registrations
container.Register<MyConcreteType>(); // Unnamed
container.Register<MyConcreteType>("Name"); // Named

// By default we register concrete types as 
// multi-instance, and interfaces as singletons
container.Register<MyConcreteType>(); // Multi-instance
container.Register<IMyInterface, MyConcreteType>(); // Singleton 

// Fluent API allows us to change that behaviour
container.Register<MyConcreteType>().AsSingleton(); // Singleton
container.Register<IMyInterface, MyConcreteType>().AsMultiInstance(); // Multi-instance 

// We also allow delegate facories and instance 
// registrations (for both concrete and interfaces)
// We default to strong references..
container.Register<MyConcreteType>((c,p) => MyConcreteType.GetNew()); // Delegate factory
var instance = MyConcreteType.GetNew();
container.Register<IMyInterface, MyConcreteType>(instance); // Instance

// ..but can use the fluent API to use
// weak references instead.
container.Register<MyConcreteType>((c,p) => MyConcreteType.GetNew()).WithWeakReference();
container.Register<IMyInterface, MyConcreteType>(instance).WithWeakReference();

// We also now support, when running on ASP.Net, per-request singletons.
// So each request will get its own singleton version of a type, but multiple
// requests don't share a single instance:
container.Register<MyConcreteType>().AsPerRequestSingleton();
container.Register<MyInterface, MyImplementation>().AsPerRequestSingleton();

// By default we try and find the best constructor
// at resolve time, but we can use the fluent API
// to specify one.
//
// This example forces this overload:
// public MyConcreteType(IMyInterface dependency, int property1, string property2)
container.Register<MyConcreteType>().UsingConstructor(
    () => new MyConcreteType(null as IMyInterface, 1, ""));

// We can also register multiple implementations of the same
// base class/interface. This is useful if you have a marker
// interface or shared base class and you need to later use
// ResolveAll to retrieve all implementations.
container.Register<IMyMarkerInterface>(new[] { typeof(MyImplementationClass1), typeof(MyImplementationClass2) });

// Registrations done this way can be made singletons (default) or multi-instance
// using a similar fluent interface to normal registrations
container.Register<IMyMarkerInterface>(new[] 
{ 
  typeof(MyImplementationClass1), 
  typeof(MyImplementationClass2) 
}).AsSingleton();
container.Register<IAnotherMarkerInterface>(new[] 
  { 
    typeof(MyOtherImplementationClass1),
    typeof(MyOtherImplementationClass2) 
  }).AsMultiInstance();

Resolution API

// Basic named and unnamed resolution
var instance = container.Resolve<MyConcreteType>(); // Unnamed
instance = container.Resolve<MyConcreteType>("Name"); // Named

// By default we try and resolve every concrete type,
// whether they are registered or not, but we don't "fallback"
// to an unnamed registration if resolving a named one.
//
// We can easily change that behaviour though.
container.Resolve<MyConcreteType>(
    new ResolveOptions() {UnregisteredResolutionAction = UnregisteredResolutionActions.Fail});
container.Resolve<MyConcreteType>("Name", 
    new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution });

// We can pass parameters using name/value pairs.
// By default the "greediest" constructor is used if possible,
// but we will fall all the way back to a default constructor if
// necessary to construct the type.
container.Resolve<MyConcreteType>(new NamedParameterOverloads() { { "property1", 12 }, { "property2", "Testing" } });

// We also have the same overloads on CanResolve
// to determine if resolution is possible
container.CanResolve<MyConcreteType>(
    new ResolveOptions() { UnregisteredResolutionAction = UnregisteredResolutionActions.Fail });
container.CanResolve<MyConcreteType>("Name",
    new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution });
container.CanResolve<MyConcreteType>(new NamedParameterOverloads() { { "property1", 12 }, { "property2", "Testing" } });

// And also for TryResolve, which will resolve if possible
// and return a boolean to indicate whether resolution completed
var resolved = container.TryResolve<MyConcreteType>("MyName", out resolvedType); // resolved is true if resolution was possible

// Resolve all resolves all registrations for a type, named or otherwise
// as IEnumerable<ResolveType>
container.Resolve<IMyInterface, MyType1>("MyType1"); // named registration
container.Resolve<IMyInterface, MyType2>("MyType2"); // 2nd named registration
IEnumerable<IMyInterface> myResolvedTypes = container.ResolveAll<IMyInterface>(); // Returns both registrations

Constructor Injection

// By default we will use the "greediest" constructor
// if we can..
public Constructors(ITestInterface1 dependency, string value1, int value2)
{
}

// .. but we will fallback if we can't - rather than
// throwing a resolution exception.
public Constructors(ITestInterface1 dependency)
{
}

// For types that are expensive to construct and may not
// always be needed we provide automatic lazy factories
// by putting Func<T> in the constructor
public Constructors(Func<MyExpensiveObject> expensiveObjectFactory)
{
    var instance = expensiveObjectFactory.Invoke();
}

// We also allow lazy factories that specify a named registration
// to resolve using Func<String, T>:
public Constructors(Func<String, MyExpensiveObject> expensiveObjectFactory)
{
    var instance = expensiveObjectFactory.Invoke("MyNamedRegistration");
}

// And we also allow you to specify named parameter overloads to lazy factories
// using Func<String, IDictionary<string, object>, T>:
public Constructors(Func<string, IDictionary<String, object>, TestClassWithParameters> factory)
{
    _Factory = factory;
    Prop1 = _Factory.Invoke("", new Dictionary<String, object> { { "stringProperty", "Testing" }, { "intProperty", 22 } });
}

Property Injection

// If constructor injection isn't possible/preferable we can also
// use property injection.
//
// Any public read/write property that we can resolve, that isn't 
// already set to a value, is resolved by the container:
class TestClassPropertyDependencies
{
    public ITestInterface Property1 { get; set; } // Will be set if we can resolve and isn't already set
    public ITestInterface2 Property2 { get; set; } // Will be set if we can resolve and isn't already set
    public int Property3 { get; set; } // Will be ignored
    public string Property4 { get; set; } // Will be ignored

    public TestClassDefaultCtor ConcreteProperty { get; set; } // Will be set if we can resolve and isn't already set

    public ITestInterface ReadOnlyProperty { get; private set; } // Will be ignored
    public ITestInterface2 WriteOnlyProperty { internal get; set; } // Will be ignored (no way to know if it's already set)
}

// -- SNIP -- //

var input = new TestClassPropertyDependencies();
container.BuildUp(input); // Properties are now set

** Child Containers **

// If we need to control scope or lifetime of particular
// registrations we can use child containers.
//
// We can get a child container from any container instance
var child = container.GetChildContainer();

// A child container will "bubble up" resolve requests if it can't resolve
// a type itself
container.Register<ITestInterface, TestClassDefaultCtor>();
var result = child.Resolve<ITestInterface>();  // Will resolve with the parent container

// If a type is registered with the child container it will resolve that
// instead of the same type registered with the parent:
container.Register<ITestInterface>(parentInstance);
child.Register<ITestInterface>(childInstance);
child.Resolve<ITestInterface>(); // Will return "childInstance"

// We can let the child container go out of scope because the 
// parent holds no reference to it. Or we can Dipose() it which 
// will DISPOSE ALL INSTANCES/SINGLETONS REGISTERED WITH IT if they
// support IDisposable:
child.Dispose(); // Disposes container and all disposable instances/singletons
Clone this wiki locally