Click here to Skip to main content
Click here to Skip to main content

Dependency Injection with ObjectPoolManager (OPM)

By , 31 Oct 2010
 

Introduction

Recently I got my hands over some of the IOC tools available for .NET and really liked the concept of dependency injection from starting stage of application and invoking / utilizing it whenever required. The only thing that was not making me using it for fun was too many complexities introduced in them over the period of time.

As conceptually this technique is not that “I can’t do it” hard, for the sake of fun, I decided to make my own.

Followings are the list of .NET IOC tools available (in order of my choices):

  1. Ninject
  2. Unity
  3. Windsor
  4. StructureMap

Definition

According to Wikipedia

“Dependency injection (DI) in object-oriented computer programming is a design pattern with a core principle of separating behavior from dependency resolution. In other words: a technique for decoupling highly dependent software components.”

Benefits

Traditionally developers used to hard code the dependencies as and when it’s required which makes that piece of code tightly coupled and needs to be changed over the period of time if any requirement changes. That certainly violates the DRY principle as the developer may need to modify the entire flow of code or make copies of methods to support only that change.

Dependency Injection could help over this situation with the implementation of Interface that provides the required functionality to end user. DI framework could load the object inherited from this interface which is actually injected much before its utilization whenever possible at runtime.

DI framework need not to be limited by this phenomenon, it actually overcomes many of the routine problems that a developer could come across. Another such example is like developer wants to utilize certain class, but is not sure about the inputs needed to make this class usable. In such a situation, the responsible module could take care of this class for providing inputs using DI and let developers use its instance just by refereeing its name or type.

According to Wikipedia

Dependency injection is a specific form of inversion of control where the concern being inverted is the process of obtaining the needed dependency.

Introducing “ObjectPoolManager” (OPM) Framework

Well it’s a lightweight dependency injector container and currently at development phase yet complete to achieve most of the common needs that every DI framework need to do. Currently this framework has been tested along a number of test cases that I thought to have in it as initial draft. Certainly it lacks some of the features that Ninject and Unity have, but I’ll keep on upgrading this as time permits. As of now, it does help to reduce boilerplate code.

Design Highlights

Design.png

The above module illustrates the current implementation of this DI framework. Whenever user injects the type or object, it maintains it inside Object container which is accessible internally by framework only. Currently this framework supports pool with and without Context. Context here means simply a catalog of types/object registered under that key. This helps user to register same types in different Contexts and with same names as well.

ObjectPoolManager Class

(Static) Maintains the object pools and contexts of pools:

Properties

Pool Provides the non-context based Object Pool
Context Collection of contexts that holds Object Pools

Methods

Clear Clears Object container

ObjectPool Class

Registers and resolves the objects:

Properties

Context Name of context pool belongs to

Methods

Register(string, object) Registers the object as singleton for provided string name.
Register<T>() Registers the class type for default constructor.
Register<T>(ObjectScope) Registers the class type for default constructor using specified scope.
Register<T>(string) Registers the class type for default constructor using specified name.
Register<T>(string, ObjectScope) Registers the class type for default constructor using specified scope string name.
Register<T>(Func<T>) Registers the class type using provided delegate.
Register<T>(Func<T>, ObjectScope) Registers the class type using provided delegate and scope.
Register<T>(string, Func<T>) Registers the class type using provided delegate and string name.
Register<T>(string, Func<T>, ObjectScope) Registers the class type using provided delegate and scope and string name.
Register<I, T>() Registers the class type T for default constructor and binds its return type to I.
Register<I, T>(ObjectScope) Registers the class type for default constructor using specified scope and binds its return type to I.
Register<I, T>(string) Registers the class type for default constructor using specified name and binds its return type to I.
Register<I, T>(string, ObjectScope) Registers the class type for default constructor using specified scope string name and binds its return type to I.
Register<I, T>(Func<T>) Registers the class type using provided delegate and binds its return type to I.
Register<I, T>(Func<T>, ObjectScope) Registers the class type using provided delegate and scope and binds its return type to I.
Register<I, T>(string, Func<T>) Registers the class type using provided delegate and string name and binds its return type to I.
Register<I, T>(string, Func<T>, ObjectScope) Registers the class type using provided delegate and scope and string name and binds its return type to I.
Resolve(string) Returns object using specified string name. This can only be used for objects registered with Register(string, object)
Resolve<T>() Returns object of type T that has been registered in current accessed pool.
Resolve<T>(string) Returns object of type T that has been registered in current accessed pool using specified string name
BeginResolve<T>(string, ObjectInvokeCallback) Begins to resolving the object registered with delegate.
Dispose() Disposes the current pool.

ObjectScope enum

None

Tells contained to create new object every time when invoked

Singleton

Returns the same object after invoking first time

ObjectInvokeArgument Class

EventArgument returned on calling BeginResolve on ObjectInvokeCallback:

Properties

Context Name of context callback called on.
Name Name used for registering the object type
Result Object returned from Async invoke

Example Application

Consider the following example implementing RR layout for Farrari F430:

    public interface IDriveLayout
    {
        string Name { get; }
    }

    public interface IEngineLayout
    {
        string Name { get; }
    }

    class RearMidEngine : IEngineLayout
    {
        public string Name
        {
            get { return "Rear Mid Engine"; }
        }
    }

    public class RearWheelDrive : IDriveLayout
    {
        public string Name
        {
            get { return "Rear Wheel Drive"; }
        }
    }

    public class Vehicle
    {
        private IDriveLayout _driveLayout;
        private IEngineLayout _engineLayout;

        public string DriveType
        {
            get { return _driveLayout.Name; }
        }

        public string EngineType
        {
            get { return _engineLayout.Name; }
        }

        public virtual string Name
        {
            get { return "Vehicle"; }
        }

        public Vehicle(IDriveLayout driveLayout, IEngineLayout engineLayout)
        {
            _driveLayout = driveLayout;
            _engineLayout = engineLayout;
        }
    }

    class FerrariF430 : Vehicle
    {
        public FerrariF430(IDriveLayout driveLayout, IEngineLayout engineLayout)
            : base(driveLayout, engineLayout)
        { }

        public override string Name
        {
            get
            {
                return "Ferrari F430";
            }
        }
    }

Registering Classes

ObjectPoolManager.Pool.Register<IDriveLayout, RearWheelDrive>("RearWheelDrive");
ObjectPoolManager.Pool.Register<IEngineLayout, RearMidEngine>("RearMidEngine");

ObjectPoolManager.Pool.Register<Vehicle, FerrariF430>("FerrariF430", () => 
                new FerrariF430(
                       ObjectPoolManager.Pool.Resolve<IDriveLayout>("RearWheelDrive"),
                       ObjectPoolManager.Pool.Resolve<IEngineLayout>("RearMidEngine")
                ));

Retrieving Classes

var vehicle = ObjectPoolManager.Pool.Resolve<Vehicle>"FerrariF430");
Console.WriteLine("{0} -> Layout: {1}, {2}", 
	vehicle.Name, vehicle.EngineType, vehicle.DriveType);
Console.ReadLine();

Also refer to Test cases included in the source code for more examples.

TODO

  1. Attribute support for constructor injection and methods injection
  2. Configurable registration support (in XML)

History

  • 31st October, 2010 – v0.8 Beta released

License

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

About the Author

Navnath_Kale
Software Developer (Senior)
India India
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberjustinonday17 Feb '11 - 22:35 
Good job .
More expecting from you about this topic
GeneralMy vote of 5memberrobvon20 Nov '10 - 13:19 
Good work an a hard topic
GeneralNice projectmemberdisore17 Nov '10 - 19:06 
Always nice to see someone take a try at creating a DI framework, as I've come to rely heavily on them.
 
I think you also should look at autofac. It has some wonderful magic going on when resolving types.
Why is it drug addicts and computer afficionados are both called users?
--Clifford Stoll

GeneralGood onememberRajesh Pillai2 Nov '10 - 7:54 
Smile | :)
Enjoy Life,
Rajesh Pillai
http://geekswithblogs.net/rajeshpillai
 


 

GeneralRe: Good onememberNavnath_Kale2 Nov '10 - 9:09 
thx Cool | :cool: Yet not the complete IOC to claim, still more things to add !!!
GeneralI too felt as you did Navnath, Prism, Unity, and the like we're cool, but......memberxzz01951 Nov '10 - 6:49 
The learning curve was too steep... I spent a week once just trying to figure out how to inject a different view in a shell... Had to abandon because I couldn't figure it out and everywhere I asked the question "How do I swap views" resulted in Nothing...
 
My own solution was to "do it the old fashioned way" with singleton factories, static methods and events. I like what you've done here and will review the code as I get a chance.
 
I believe personally that DI is one of the major tools in the toolbox. And that the support for it is just getting underway in MSFT world. Smile | :)
GeneralRe: I too felt as you did Navnath, Prism, Unity, and the like we're cool, but......memberNavnath_Kale2 Nov '10 - 6:45 
Thanks that you liked it Smile | :)
GeneralMmmmm [modified]mvpSacha Barber1 Nov '10 - 1:53 
I am a bit confused, from your article you write something that makes it look like your code resolves all types in a chain, yet here is your Resolse method
 

  public T Resolve<T>() 
            where T : class
        {
            return Resolve<T>(string.Empty);
        }
 
        public T Resolve<T>(string name) 
            where T : class
        {
            var objectKey = new ObjectKey
            {
                ObjectType = typeof (T),
                Name = name,
                Context = this.Context,
                Instantiated = false
            };
 
            if (ObjectPoolManager.ObjectFactory.ContainsKey(objectKey))
            {
                var objectInstance = ObjectPoolManager.ObjectFactory[objectKey];
 
                T returnObject = null;
                var func = objectInstance.Invoker as Delegate;
 
                returnObject = (objectInstance.Scope == ObjectScope.Singleton ? objectInstance.Instance as T : null) ?? 
                    (func != null ? func.DynamicInvoke() as T: null);
 
                if (objectInstance.Scope == ObjectScope.Singleton)
                    objectInstance.Instance = returnObject;
 
                return returnObject;
            }
 
            return null;
        }
 
How does that resolve dependencies?
 

Ok reread it and I can see what you are doing. Following this unit test
 
ObjectPoolManager.Pool.Register<IDriveLayout, RearWheelDrive>("RearWheelDrive");
ObjectPoolManager.Pool.Register<IEngineLayout, RearMidEngine>("RearMidEngine");
 
ObjectPoolManager.Pool.Register<Vehicle, FerrariF430>("FerrariF430", () => new FerrariF430(
                                                                    ObjectPoolManager.Pool.Resolve<IDriveLayout>("RearWheelDrive"),
                                                                    ObjectPoolManager.Pool.Resolve<IEngineLayout>("RearMidEngine")
                                                                )
                                                           );
 
var vehicle = ObjectPoolManager.Pool.Resolve<Vehicle>("FerrariF430");
Console.WriteLine("{0} -> Layout: {1}, {2}", vehicle.Name, vehicle.EngineType, vehicle.DriveType);
Console.ReadLine();
 
You are just using the Func delegate to return the nested types IDriveLayout/IEngineLayout for Vehicle. Thing is this relies on you setting up all your dependencies when you register.
 
Contrast this with say Castles approach
 

var container = new WindsorContainer();
 
// adds and configures all components using WindsorInstallers from executing assembly
container.Install(FromAssembly.This());
 
// instantiate and configure root component and all its dependencies and their dependencies and...
var king = container.Resolve<IKing>();
 
That deals with all dependencies also.
 
Still not a bad little article you wrote overall
Sacha Barber
  • Microsoft Visual C# MVP 2008-2010
  • Codeproject MVP 2008-2010
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net
modified on Monday, November 1, 2010 7:59 AM

GeneralRe: MmmmmmemberNavnath_Kale1 Nov '10 - 2:16 
Good that you figured it out by your own Smile | :)
 
Regarding Func thats just a one part of OPM you checked. Certainly it does very lil part of what most of available DI tool does today. But hey as I said I'll add attribute support soon and till then take care of what you shown with Windsor Smile | :)
GeneralRe: MmmmmmemberNavnath_Kale2 Nov '10 - 9:08 
Actually I didn't wrote code to look like "it resolves all dependencies in chain". At least i didn't advertised it that way.
 
Also Windsor as you mentioned doesn't go inside assembly and bring types that you ask. You still need to register it somehow or at least provide the configuration. see below extract from http://stw.castleproject.org/Default.aspx?Page=Installers&NS=Windsor&AspxAutoDetectCookieSupport=1
 
Instead of instantiating installers manually you can leave this up to Windsor by using FromAssembly class. You point it to an assembly and it will then instantiate and install all installer types from that assembly for you.
 
This will only install Installers from provided assembly not just any type inside that assembly. To register types you need to do something for example though code like this (but i like it through config)
 
01	public class RepositoriesInstaller : IWindsorInstaller 
02	{ 
03	   public void Install(IWindsorContainer container, IConfigurationStore store) 
04	   { 
05	      container.Register(AllTypes.FromAssemblyNamed("Acme.Crm.Data") 
06	                            .Pick() 
07	                            .If(Component.IsInNamespace("Acme.Crm.Repositories")) 
08	                            .If(type => type.Name.EndsWith("Repository")) 
09	                            .Configure(c => c.LifeStyle.PerWebRequest)); 
10	   } 
11	}
 
At least think this way "When I want any class to be instantiated who will decide which constructor will get executed?"
 
Makes sense ? I would like to hear more from your side
 
Cheers!!! Cool | :cool:
GeneralsuggestionmemberSeishin#31 Oct '10 - 22:24 
use custom Attributes to mark classes to register
implement a manager class which will automatically pick those marked classes and register them..
pattern can be used in many other applications like binding commands with triggers..
life is study!!!

GeneralRe: suggestionmemberNavnath_Kale31 Oct '10 - 22:59 
Thx for your reply actually its in TODO list. I'll implement it soon...
 
Rate it if you liked Smile | :)

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 1 Nov 2010
Article Copyright 2010 by Navnath_Kale
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid