Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C# 4.0

BarbarianIOC : A simple IOC Container

Rate me:
Please Sign up or sign in to vote.
4.99/5 (39 votes)
25 Feb 2013CPOL15 min read 72.4K   520   73   36
A simple IOC Container just for the fun of it really

 

Article demo code : BarbarianIOC.zip

 

Introduction

At work I have used a variety of IOC Containers including

And I have used others such as Unity/StructureMap/AutoFac. They are all very  good and very rich.

For those of you who don't know what IOC stands for, it stands for Inversion  Of Control. Which is described as follows: 

In software engineering, inversion of control (IoC) is a programming  technique, expressed here in terms of object-oriented programming, in which  object coupling is bound at run time by an assembler object and is typically not  known at compile time using static analysis.

In traditional programming, the flow of the business logic is determined  by objects that are statically assigned to one another. With inversion of  control, the flow depends on the object graph that is instantiated by the  assembler and is made possible by object interactions being defined through  abstractions. The binding process is achieved through dependency injection,  although some argue that the use of a service locator also provides inversion of  control.

In order for the assembler to bind objects to one another, the objects  must possess compatible abstractions. For example, class A may delegate behavior  to interface I which is implemented by class B; the assembler instantiates A and  B then injects B to A.

In practice, inversion of control is a style of software construction  where reusable code controls the execution of problem-specific code. It carries  the strong connotation that the reusable code and the problem-specific code are  developed independently, which often results in a single integrated application.  Inversion of control as a design guideline serves the following purposes:

  • There is a decoupling of the execution of a certain task from  implementation.
  • Every module can focus on what it is designed for.
  • Modules make no assumptions about what other systems do but rely on  their contracts.
  • Replacing modules has no side effect on other modules.
  • Inversion of control is sometimes facetiously referred to as the  "Hollywood Principle: Don't call us, we'll call you", because program logic  runs against abstractions such as callbacks.

Wikipedia : up on date 25/02/2013

Thing is I have always wanted to try and make one of these  myself, just to see what is involved. I did not want to go too nuts on this, and  just wanted the following really:

  1. Instance configuration : singleton / transient
  2. Simple registration process, maybe some sort of fluent interface
  3. Use the Expression API to compile into delegates for quick creation of  objects
  4. Constructor / property injection
  5. Provide the ability to accept non IOC held constructor parameters

So those couple of points are ALL I wanted to get working. As I say there are  a whole slew of full fledged IOC containers out there (where I have named a few  above), this articles container is more of a learning exercise, that I thought  I would share, in case anyone else is interested in this sort of thing.

I am calling my container BarbarianIOC as the existing  containers all seems to have these short snappy names, and it's kind of play on  my name, and if you saw me without a shave I do kinda look a bit like a  barbarian.

So there we go. That's essentially what this article is about, but just  before we get into the guts of it, please read this important note below.

IMPORTANT NOTE

I should point out that you should stick to using one of the major IOC containers out there,  as this was an exercise  to see what you needed to do to create your own. That is not to say I am not  happy with it, I totally am, and I think with more tinkering, I could make it  act near enough like one of the "proper" IOC containers out there, but I just know that,  that tinkering will never happen, as I am always  eager to move on to something new. So yeah just stick to using one of the big  "proper" IOC containers out there.

 

What Does It Do

In this section I will should you how to use BarbarianIOC, and  what that looks like in a typical application

How Do You Configure The Container

The container can be configured using a fluent like interface something like  this:

C#
Container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)

//Register ALL components
container.RegisterComponents(
        //where you can use concrete type
        new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
        //or you can use an interface and it's implementation
        new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
        //and you can also declare singleton instance mode if you like
        new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
        //and even supply some non IOC provided constructor params by way of an anonymous object
        new Component().For<SomeFooDependantClass>()
            .DependsOn(new 
                { 
                    age=someMockAge
                })
            .WithInstanceMode(InstanceMode.Transient)
    );
            
//allow the container to wire stuff up (essentially create Expression.New for all 
//components to allow Container to compile and create some quicker lookup delegates)
container.WireUp();

 

Allow For Non IOC Constructor Parameters

One of the important issues I wanted to tackle was to not only allow  container generated constructor parameters to be provided, but also user  specified  constructor parameters. I decided to do this using a simple anonymous object,  where you just specify the name of the constructor parameter and its value. An  example of this is shown below, where we can provide these extra non IOC constructor parameters when we  configure the IOC container as follows:

C#
container.RegisterComponents(
        new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
        new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
        new Component().For<SomeFooDependantClass>()
            .DependsOn(new 
                { 
                    age=someMockAge
                })
            .WithInstanceMode(InstanceMode.Transient)
    );

Note the use of the DependsOn(..) method and the anonymous object  which provides the age NON IOC created constructor parameter, which  may be needed by some class as shown below:

C#
public class SomeFooDependantClass
{
    /// <summary>
    /// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
    /// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
    /// Type from the BarbarianIOC.Container
    /// </summary>
    [DependencyConstructorAttribute]
    public SomeFooDependantClass(Foo foo, int age, IBaz baz)
    {


    }
}

This shows that we have a class that expects an age constructor parameter  value, along with some other constructor parameters that SHOULD come from the  IOC container. Note the order of the non IOC generated constructor parameter is  in the middle of the constructor arguments, it can be anywhere, full mix and  match of IOC/non IOC created constructor parameters is supported. I was pleased  with that feature.

 

Constructor Injection

The IOC container that goes with this article allows for constructor  injection, thus it is able to satisfy the following constructor

C#
public class SomeIBazDependantClass
{
    private IBaz somePropBaz;

    [DependencyConstructorAttribute]
    public SomeIBazDependantClass(IBaz baz)
    {

    }
}

As previously stated it is also possible to include extra NON IOC constructor params such as the age  parameter we just saw. Here is an example of how you would annotate your class  to tell it which constructor it was going to try and satisfy dependencies for.

C#
public class SomeFooDependantClass
{
    /// <summary>
    /// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
    /// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
    /// Type from the BarbarianIOC.Container
    /// </summary>
    [DependencyConstructorAttribute]
    public SomeFooDependantClass(Foo foo, int age, IBaz baz)
    {


    }
}

It can be seen from the code above, that I have chosen to use a specialized  attribute called DependencyConstructorAttribute which you WILL need  to use, to mark up your chosen constructor with. This will indicate to the IOC  container presented with this article, which constructor should be used to create  an object from the container.

Now there may be some amongst you, who see this as a violation of Separation Of Concerns (SOC), and to be honest it is  slightly, as our objects now contain IOC specific data in them, by way of these  special DependencyConstructorAttributes. However without this

DependencyConstructorAttribute
, which ConstructorInfo to use  would be quite an error prone experience. Should the IOC container go for the  one with the most parameters / the least parameters / the one that has the most  IOC resolvable parameters. It's not an easy decision. I could have ade some  fancy fluent API registration code for it, but in the end I feel justified  in my use of a specialized attribute (DependencyConstructorAttribute),  which the user can use to adorn their objects, which tells the IOC container,  hey you need to create an object using this constructor.

The order of the IOC / non IOC constructor parameters is NOT important, you can mix and match IOC / non IOC provided constructor parameters as much as you like . We will get on to how this works later.

 

Property Injection

The IOC container that goes with this article also allows for property  injection, which can be configured as follows:

C#
public class SomeIBazDependantClass
{
    private IBaz somePropBaz;

    /// <summary>
    /// BarbarianIOC also allows for property injection (provided you use the 
    /// <see cref="BarbarianIOC.Attributes.DependencyAttribute">DependencyAttribute</see>)
    /// </summary>
    [Dependency]
    public IBaz SomePropBaz
    {
        get 
        { 
            return somePropBaz; 
        }
        set 
        { 
            somePropBaz = value; 
        }
    }
}

As with the constructor injection, I opted for using a specialized attribute DependencyAttribute, which indicates which properties the IOC  container should try and inject.

 

Resolving Instances

When you need a object instance from the container all you need to do is Resolve it which is done as follows :

C#
SomeIBazDependantClass x1 = container.Resolve<SomeIBazDependantClass>();

From there you should be able to use the object instance which will have had all its constructor/property IOC/Non  IOC dependencies resolved automatically (providing you got the component  registration configuration correct).

Here is an example of an object that was resolved from the container code  attached to this article, you can clearly see it has all the  properties/constructor vaues provided.

Image 1

 

How Does It Work

Ok so now I have shown you how to use it, but I bet you want to know how it  works. Well lucky for you, in this section I will talk you through the code and you can get to see the nitty gritty  inner workings. Hopefully that will be interesting.

Registering Instances

Registering components (whether they be service instances or concrete types)  is all done using a fluent interface which starts with the

Component
builder, which is used to create a ComponentRegistration instance. The following method(s) are available to aid in the building of a
ComponentRegistration 
entry:

  • Component For<TCOMP>() : used for registering  a concrete type, and is responsible for creating  a instance
  • Component ServiceFor<IInt, TCOMP>() : used for registering a  service which implements a certain interface
  • Component DependsOn<T>(T dependencies) : used for supplying  non IOC container satisfied constructor parameter(s)
  • ComponentRegistration WithInstanceMode(InstanceMode instanceMode)  : which MUST be the final method called in the
    Component
    
    builder fluent API  process and it responsible for creating the final
    ComponentRegistration 
    instance.

 

So with that in mind, lets see how the IOC container uses these

ComponentRegistration 
objects. This is all down to 1 simple method called RegisterComponents which looks like this, which simply takes the  registrations and adds them to a Container held collection

C#
public void RegisterComponents(params ComponentRegistration[] registrations)
{
    lock (syncLock)
    {
        foreach (ComponentRegistration componentRegistration in registrations.ToList())
        {
            components.Add(componentRegistration, null);
        }
    }
}

So now that we know we are striving to create

ComponentRegistration
objects which are passed to the Container, but what do these 
ComponentRegistration
objects look like, well they look like this:

C#
public class ComponentRegistration
{
    public Type TypeToLookFor { get; private set; }
    public Type TypeToCreate { get; private set; }
    public InstanceMode InstanceMode { get; set; }
    public bool HasManualConstructorParameters { get; set; }
    public List<ConstructorParameterDependency> DependsOnValues { get; set; }

    public ComponentRegistration(Type typeToCreate) : this(typeToCreate, typeToCreate)
    {


    }

    public ComponentRegistration(Type typeToLookFor, Type typeToCreate)
    {
        TypeToLookFor = typeToLookFor;
        TypeToCreate = typeToCreate;
        DependsOnValues = new List<ConstructorParameterDependency>();
    }
}

Let's now  continue to look at the specific Component builder methods.

 

Registering Concrete Instances

This is how you would typically register a concrete type using the Component  builder methods

new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient)

So now let's look at how this works, this is pretty easy and is responsible  for creating a instance of a

ComponentRegistration 
that will be returned at the end of the
Component 
fluent API, so where you see the "return this"  within the next couple of methods, that is the fluent API in actio

C#
public Component For<TCOMP>()
{
    componentRegistration = new ComponentRegistration(typeof(TCOMP));
    return this;
}

Registering Service Instances

This is how you would typically register a interface and implementation type using the Component  builder methods

new Component().ServiceFor<IBaz,Baz>().WithInstanceMode(InstanceMode.Transient)

So now let's look at how this works, this is pretty easy and is responsible  for creating a instance of a

ComponentRegistration 
that will be returned at the end of the
Component 
fluent API.

C#
public Component ServiceFor<TInt, TCOMP>()
{
    componentRegistration = new ComponentRegistration(typeof(TInt), typeof(TCOMP));
    return this;
}

 

Specifying The Instance Mode

The ComponentRegistration WithInstanceMode(InstanceMode instanceMode)  is the simplest of all the Component builder methods, and simply  sets the InstanceMode on the ComponentRegistration that it is  currently building, and then returns the current

ComponentRegistration
being built.

Here is ALL the code for the 

ComponentRegistration 
WithInstanceMode(InstanceMode instanceMode) Component
builder method

C#
public ComponentRegistration WithInstanceMode(InstanceMode instanceMode)
{
    if (componentRegistration == null)
    {
        throw new ContainerConfigurationException("Configuration error WithInstanceMode<> MUST be last");
    }
    else
    {
        componentRegistration.InstanceMode = instanceMode;
        return componentRegistration;
    }
}

 

Providing Extra NON IOC Constructor Parameters

One of the things that I really wanted to achieve was the ability to satisfy  constructor parameters that were not meant to come from the IOC container. In  essence what I wanted was the ability to mix and match IOC container satisfied  constructor parameter(s) with non IOC container satisfied constructor  parameter(s). Where non IOC container satisfied constructor parameter(s) may be  things like

  • App settings
  • Connections strings
  • Static values

As before we use the Component fluent interface to build up a ComponentRegistration object. The relevant Component builder method for adding non IOC container satisfied constructor parameter(s)  is the DependsOn( ) method that is shown below.

C#
public Component DependsOn<T>(T dependencies)
{
    if (componentRegistration == null)
    {
        throw new ContainerConfigurationException(
            "Configuration error DependsOn<> MUST be called after For<> or ImplementedBy<>");
    }
    else
    {
        List<ConstructorParameterDependency> constructorDependencies = 
		new List<ConstructorParameterDependency>();
        foreach (string name in typeof(T).GetConstructors()[0].GetParameters()
                                        .Select(p => p.Name))
        {
            PropertyInfo property = typeof(T).GetProperty(name);
            ParameterExpression param = Expression.Parameter(typeof(T), "x");
            Expression propertyAccess = Expression.Property(param, property);
            Expression convert = Expression.Convert(propertyAccess, typeof(object));
            Func<T, object> lambda = 
		Expression.Lambda<Func<T, object>>(convert, param).Compile();
            var result = lambda(dependencies);
            constructorDependencies.Add(new ConstructorParameterDependency(
                property.PropertyType, name, Expression.Constant(result)));
        }

        if (constructorDependencies.Any())
        {
            componentRegistration.HasManualConstructorParameters = true;
            componentRegistration.DependsOnValues = constructorDependencies;
        }
        else
        {
            componentRegistration.HasManualConstructorParameters = false;
        }
        return this;
    }
}

The basic idea here is that we examine an anonymous object that was passed  into the DependsOn( ) method, and we build up a List<ConstructorParameterDependency>  values for each of the properties/property values found on the anonymous object  that was passed into the DependsOn( ) method. These are later  examined by the IOC container to work out which constructor parameter values  should come from where. Essentially the IOC container orders the

ConstructorParameterDependency
based on their Position property.

We could have used some reflection here instead, as these constructor  parameter values are essentially cached once the 1st instance of a certain type  has been requested from the IOC container, so reflection may have been a better  choice here instead of using Expression trees, but since everything else is  Expression based I stuck with it. The performance difference in this case would  be minute, so I stuck to using the Expression APIs.

We have already seen an example of the usage of this, but just for  completeness here it is again:

C#
//Register ALL components
container.RegisterComponents(
    //and even supply some non IOC provided constructor params by way of an anonymous object
    new Component().For<SomeFooDependantClass>()
        .DependsOn(new 
            { 
                age = someMockAge
            })
        .WithInstanceMode(InstanceMode.Transient)
);

 

How The Instance Mode Works

At present the IOC container associated with this article only supports 2  instance modes:

  1. Singleton : Every instance of the object resolved with this instance  mode is expected to be the exact same instance
  2. Transient : Every instance of the object resolved with this instance  mode is expected to be a new unique instance

Transient Instance Mode

The transient mode uses a simple factory which uses a delegate that the IOC container has cached. Where the delegate (Expression.New is compiled into a delegate)  is invoked, to return a new object instance. The TransientFactory can be seen  below:

C#
/// <summary>
/// Transient object factory which returns a new instance 
/// when Create() method is called
/// </summary>
public class TransientFactory : IFactoryProvider
{
    private Delegate objectCreator;


    public TransientFactory(Delegate objectCreator)
    {
        this.objectCreator = objectCreator;
    }


    public object Create()
    {
        return objectCreator.DynamicInvoke();
    }
}

Singleton Instance Mode

The singleton mode also uses a simple factory which essentially uses a delegate that the IOC container has cached. Where the delegate (Expression.New is compiled into a delegate)  is used as the value of a Lazy<T> (which is  really a cheeky way of creating a thread safe singleton), where the new object  instance returned by invoking the delegate used as the Lazy<T>.Value. The SingletonFactory can be seen  below:

C#
/// <summary>
/// Singleton object factory which returns the same singleton instance 
/// when Create() method is called
/// </summary>
public class SingletonFactory : IFactoryProvider
{
    private Lazy<object> singletonCreator;

    public SingletonFactory(Delegate objectCreator)
    {
        singletonCreator = new Lazy<object>(() => objectCreator.DynamicInvoke());
    }

    public object Create()
    {
        return singletonCreator.Value;
    }
}

Putting This To The Test

Based on what we just talked about above, lets assume we have the following  IOC container configuration

C#
Container container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)

//Register ALL components
container.RegisterComponents(
    //where you can use concrete type
    new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
    //or you can use an interface and it's implementation
    new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
    //and you can also declare singleton instance mode if you like
    new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
    //and even supply some non IOC provided constructor params by way of an anonymous object
    new Component().For<SomeFooDependantClass>()
        .DependsOn(new 
            { 
                age=someMockAge
            })
        .WithInstanceMode(InstanceMode.Transient)
);

Which when we examine the simple tests as following we get the results we want/expect based on the configuration above (Click below for bigger image)

Image 2

 

Wiring It All Together

Once you have registered all your Component(s) you need to tell  the container to wire everything together. What this does in a nutshell is to  create a Expression.Constant delegate for each of the constructor parameters making sure to adhere to the  InstanceMode that was requested, and to also create a final  delegate that is responsible for newing up the registering type, that will be  used by the Resolve<T> method.

It does this recursively until all  the constructor dependencies of all the register components have matching  delegates to create their constructor parameters, and that there is a final  delegate that is responsible for newing up the registering type again taking  into account the InstanceMode that was requested.

This is only done once per type. What we then end up with, is a bunch of  constructor parameter value creation delegates (factories essentially) that we  use to create List<ConstructorParameterDependency> (the same as the  Non IOC generated constructor parameters) for each of the IOC required  constructor parameters. This is then merged with any

 
ConstructorParameterDependency 
instances that were created by using the DependsOn(..) method.

The final step is to just sort the

List<ConstructorParameterDependency>
using their Position property, and to use an Expression.New inside a  Expression.Lambda to create a factory for creating the instance that is being  examined. This will also take into account the InstanceMode.

The most relevant methods of the Container are shown below:

C#
public void WireUp()
{
    foreach (ComponentRegistration key in components.Where(c => c.Value == null).Select(c => c.Key).ToList())
    {
        CreateFactory(key, GetConstructorDelegateForType(key.TypeToCreate), key.InstanceMode);
    }
}

private Delegate GetConstructorDelegateForType(Type type)
{
    ComponentRegistration componentRegistration = null;

    //look for constructor that is marked with DependencyConstructorAttribute, 
    //if there is none just try and go for the default constructor
    ConstructorInfo ctor = type.GetConstructors()
        .Where(x => x.GetCustomAttributes(typeof(DependencyConstructorAttribute), false).Count() > 0).SingleOrDefault();

    if (ctor == null)
    {
        ctor = type.GetConstructors()[0];
    }

    foreach (var ctorArg in ctor.GetParameters())
    {
        bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
        if (!isParamCoveredByManualRegistration)
        {
            bool parameterKeyFound = components.Keys.Any(x => x.TypeToCreate == ctorArg.ParameterType || 
                ctorArg.ParameterType.IsAssignableFrom(x.TypeToLookFor));
                    
            if (!parameterKeyFound)
            {
                throw new ContainerConfigurationException(string.Format("Couldn't find ctor argument {0}", ctorArg.GetType()));
            }
            else
            {
                componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
                if (components[componentRegistration] == null)
                {
                    Delegate delegateForType = GetConstructorDelegateForType(componentRegistration.TypeToCreate);
                    CreateFactory(componentRegistration, delegateForType, componentRegistration.InstanceMode);
                }
            }
        }
    }

    List<ConstructorParameterDependency> args = new List<ConstructorParameterDependency>();
    foreach (var ctorArg in ctor.GetParameters())
    {
        bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
        if (!isParamCoveredByManualRegistration)
        {
            componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
            args.Add(new ConstructorParameterDependency(
                ctorArg.ParameterType,
                ctorArg.Name,
                Expression.Constant(components[componentRegistration].Create()),
                ctorArg.Position));
        }
    }

    componentRegistration = FetchComponentRegistration(type);
    if (componentRegistration != null)
    {
        if (componentRegistration.DependsOnValues.Any())
        {
            args.AddRange(componentRegistration.DependsOnValues);
        }
    }

    return Expression.Lambda(Expression.New(ctor, args.OrderBy(x => x.Position)
        .Select(x => x.Value).ToArray())).Compile();
}


private bool IsParamCoveredByManualRegistration(Type constructorOwnerType, ParameterInfo constructorArg)
{
    ComponentRegistration componentRegistration = FetchComponentRegistration(constructorOwnerType);
    if (!componentRegistration.HasManualConstructorParameters)
    {
        return false;
    }
    else
    {
        ConstructorParameterDependency constructorParameterDependency =
            componentRegistration.DependsOnValues.SingleOrDefault(
                x => x.ArgType == constructorArg.ParameterType && x.Name == constructorArg.Name);

        if (constructorParameterDependency != null)
        {
            constructorParameterDependency.Position = constructorArg.Position;
            return true;
        }
        else
        {
            return false;
        }
    }
}


private void CreateFactory(ComponentRegistration key, Delegate @delegate, InstanceMode instanceMode)
{
    IFactoryProvider factoryProvider = null;
    
    if (instanceMode == InstanceMode.Transient)
    {
        factoryProvider = new TransientFactory(@delegate);
    }
    
    if (instanceMode == InstanceMode.Singleton)
    {
        factoryProvider = new SingletonFactory(@delegate);
    }

    lock (syncLock)
    {
        components[key] = factoryProvider;
    }
}

How Resolving Instance Works

The resolving of instances is probably the simplest part, as all the hard  work should have already been done by the WireUp() method, who's job  it is to ensure that there is a IFactoryProvider created for each  registered Component. So resolving an object from the container  really just boils down to these 3 simple steps

  1. Find the ComponentRegistration that has the correct
    TypeToCreate
    
    as the requested generic type to the Resolve<T>( )  method
  2. Use the IFactoryProvider to create an instance of the  correct type (where the IFactoryProvider would have already  been created by the WireUp() method), or throw an
    Exception
    
    if we can't find a IFactoryProvider. If we  get to the point where we chuck an Exception here, this may be  due to a missing or bad ComponentRegistration.
  3. If we get a new object created by the IFactoryProvider,  simply go through its properties that are marked up with the
    [Dependency]
    
    attribute and satisfy them from the IOC container. Once  this is done simply return the object which should have now had the  following done to it:
    • IOC generated constructor parameters should have been satisfied
    • Non IOC constructor parameters should have been satisfied
    • Properties that are marked as IOC dependencies (ones with
      [Dependency]
      
      attribute) should also have been  satisfied

This can be seen within the 2 methods shown below:

C#
public T Resolve<T>()
{
    lock (syncLock)
    {
        IFactoryProvider creator = components.Where(x => x.Key.TypeToCreate == typeof(T)).Select(x => x.Value).SingleOrDefault();

        if (creator != null)
        {
            T newlyCreatedObject = (T)creator.Create();
            SatisfyProperties<T>(newlyCreatedObject);
            return newlyCreatedObject;
        }
        else
        {
            throw new ContainerConfigurationException(string.Format(
                "Couldn't create instance of {0} could not find correct IFactoryProvider. This may be down to missing Component registration", 
                typeof(T).FullName));
        }
    }
}


private void SatisfyProperties<T>(T newlyCreatedObject)
{
    foreach (PropertyInfo prop in newlyCreatedObject.GetType().GetProperties()
        .Where(x => x.GetCustomAttributes(typeof(DependencyAttribute), false).Count() > 0))
    {
        IFactoryProvider factoryProvider = components.Single(x => x.Key.TypeToCreate == prop.PropertyType || 
            prop.PropertyType.IsAssignableFrom(x.Key.TypeToLookFor)).Value;

        if (factoryProvider != null)
        {
            prop.SetValue(newlyCreatedObject, factoryProvider.Create(), null);
        }
        else
        {
            throw new ContainerConfigurationException(string.Format(
                "Couldn't find instance of {0} to use for property injection", prop.PropertyType.FullName));
        }
    }
}

 

That's It

Anyway that's it. I kind of wrote this one just for fun really. As I say I would  really not recommend anyone use this, as there are some seriously good IOC containers out there, and I would stick to using one of them. This one is a mere play thing to gain some insight as to how one might go about building a simple IOC container, where you may not be able to use 3rd party Dlls, or just want something dead simple.

Anyway like I say I just wrote this one for fun really, and if you feel like leaving a  comment / vote that would be most welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
Chinmaya C28-Apr-20 15:51
Chinmaya C28-Apr-20 15:51 
Questionvery nice Pin
BillW3311-Dec-13 3:22
professionalBillW3311-Dec-13 3:22 
QuestionNon IOC params... Pin
SledgeHammer0123-Mar-13 18:55
SledgeHammer0123-Mar-13 18:55 
AnswerRe: Non IOC params... Pin
Sacha Barber23-Mar-13 22:19
Sacha Barber23-Mar-13 22:19 
QuestionMy vote of 5 Pin
seesharper20-Mar-13 12:30
seesharper20-Mar-13 12:30 
AnswerRe: My vote of 5 Pin
Sacha Barber23-Mar-13 22:21
Sacha Barber23-Mar-13 22:21 
AnswerRe: My vote of 5 Pin
Sacha Barber23-Mar-13 22:24
Sacha Barber23-Mar-13 22:24 
GeneralMy vote of 5 Pin
Florian Rappl11-Mar-13 23:10
professionalFlorian Rappl11-Mar-13 23:10 
GeneralRe: My vote of 5 Pin
Sacha Barber11-Mar-13 23:58
Sacha Barber11-Mar-13 23:58 
GeneralGood Work Pin
Shahriar Iqbal Chowdhury/Galib8-Mar-13 10:17
professionalShahriar Iqbal Chowdhury/Galib8-Mar-13 10:17 
GeneralRe: Good Work Pin
Sacha Barber8-Mar-13 10:39
Sacha Barber8-Mar-13 10:39 
GeneralWell done, Sacha Pin
Espen Harlinn5-Mar-13 11:44
professionalEspen Harlinn5-Mar-13 11:44 
GeneralRe: Well done, Sacha Pin
Sacha Barber6-Mar-13 6:46
Sacha Barber6-Mar-13 6:46 
GeneralRe: Well done, Sacha Pin
Espen Harlinn6-Mar-13 9:33
professionalEspen Harlinn6-Mar-13 9:33 
GeneralRe: Well done, Sacha Pin
Sacha Barber6-Mar-13 10:09
Sacha Barber6-Mar-13 10:09 
GeneralRe: Well done, Sacha Pin
Espen Harlinn6-Mar-13 10:11
professionalEspen Harlinn6-Mar-13 10:11 
GeneralRe: Well done, Sacha Pin
Sacha Barber6-Mar-13 6:47
Sacha Barber6-Mar-13 6:47 
GeneralRe: Well done, Sacha Pin
Monjurul Habib6-Mar-13 7:06
professionalMonjurul Habib6-Mar-13 7:06 
GeneralRe: Well done, Sacha Pin
Shahriar Iqbal Chowdhury/Galib8-Mar-13 10:13
professionalShahriar Iqbal Chowdhury/Galib8-Mar-13 10:13 
GeneralMy vote of 5 Pin
Monjurul Habib4-Mar-13 19:18
professionalMonjurul Habib4-Mar-13 19:18 
GeneralRe: My vote of 5 Pin
Sacha Barber6-Mar-13 6:49
Sacha Barber6-Mar-13 6:49 
GeneralMy vote of 5 Pin
Kanasz Robert3-Mar-13 22:46
professionalKanasz Robert3-Mar-13 22:46 
GeneralRe: My vote of 5 Pin
Sacha Barber6-Mar-13 6:48
Sacha Barber6-Mar-13 6:48 
Question[Dependency] attribute vs a call to the DI container Pin
Paulo Zemek1-Mar-13 9:44
mvaPaulo Zemek1-Mar-13 9:44 
AnswerRe: [Dependency] attribute vs a call to the DI container Pin
Sacha Barber2-Mar-13 4:37
Sacha Barber2-Mar-13 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.