Tuesday, March 16, 2010

Inversion of Control - Structure Map

 have been evaluating Inversion of Control (IoC) containers and offerings lately to determine a good fit for a new project in Wpf.  I have looked at UnityStructureMap, and Windsor (although only really a glance at Windsor). I've read a few articles as well comparing a few and there seems to be a consensus with like minded people like myself (fans of DSL style command-chaining and Lambda) in favour of StructureMap. Another reason to like StructureMap is the evident effort to keep pace with language changes and constant improvement in finding more concise ways to implement the IoC pattern.


Terminology:
I use the term factory to abbreviate IoC Container.

The only thing left to decide is how to configure it and which injection strategy to use.  

[Edit 10-June-2010]


Configuration
Options are:
Configuration by app.config, xml, or code.  
This seems to be an easy answer in my opinion.  If something needs to be customisable per user / install then that registration must go into the app.config / xml, otherwise code registration is much faster. Code registration also ensures everyone on the team gets new registration entries as my team does not share app.config files (developer preferences interfere).


    1 <?xml version="1.0" encoding="utf-8" ?>
    2 <configuration>
    3     <configSections>
    4         <section name="StructureMap" type="StructureMap.Configuration.StructureMapConfigurationSection,StructureMap"/>
    5     </configSections>
    6 
    7     <StructureMap>
    8         <DefaultInstance
    9             PluginType="Framework.Logging.ILogWriter,Framework"
   10             PluggedType="Framework.Logging.Log4NetLogWriter,Framework"
   11             />
   12         <DefaultInstance
   13             PluginType="StructureMap.Testing.Widget.IRule,StructureMap.Testing.Widget"
   14             PluggedType="StructureMap.Testing.Widget.ColorRule,StructureMap.Testing.Widget"
   15             Color="Blue"
   16             Scope="Singleton"
   17             Name="TheBlueOne"/>
   18     </StructureMap>
   19 </configuration>
Example app.config.


    6             var existingInstance = new Widget();
    7             ObjectFactory.Initialize(init => {
    8                 init.For<ILogWriter>().Use<FakeLogWriter>();
    9                 init.For<IService>().Singleton().Use<MyService>();
   10                 init.For<IWidget>().Use(existingInstance);
   11                 init.For<IComplex>().Use(() => new ComplexToBuildObject());
   12             });
Example code configuration.

Usage Strategies:
This where it gets more complicated, and probably purely personal preference.  Options are:
  • Constructor Injection
  • Setter Injection
  • Lambda / Function builders
Constructor Injection
The most common usage is Constructor Injection.  This involves injecting dependencies into a public constructor at object creation time.

   42     public class TestConstructor {
   43         public TestConstructor(ITestSingleton singletonITestTransient transient) {
   44             this.SingletonProperty = singleton;
   45             this.TransientProperty = transient;
   46         }
   47 
   48         public ITestTransient TransientProperty { getprivate set; }
   49 
   50         public ITestSingleton SingletonProperty { getprivate set; }
   51     }
       



   18     [TestFixture]
   19     public class TestConstructorInjection {
   20         [Test]
   21         public void ConstructorInjection() {
   22             var factory = new Container(config => {
   23                 config.For<ITestSingleton>().Singleton().Use<TestSingleton>();
   24                 config.For<ITestTransient>().Use<TestTransient>();
   25             });
   26 
   27             var target = factory.GetInstance<TestConstructor>();
   28 
   29             Assert.IsNotNull(target.TransientProperty);
   30             Assert.IsInstanceOf<TestTransient>(target.TransientProperty);
   31             Assert.IsNotNull(target.SingletonProperty);
   32             Assert.IsInstanceOf<TestSingleton>(target.SingletonProperty);
   33         }
   34     }

Pros / Cons:
+ Not bad speed performance (see further on in this post)
+ Small amount of registration code (1 per dependency globally - ie if multiple classes use ITestSingleton no more registrations are required).
- Requires public constructor even if you do not want the class createable externally.

Setter Injection:
This is less common, but may be more desirable now with C# Object Initialisers favoured over constructors to simply set properties.  

   18     public class ClassWithPropertyDependencies {
   19         [SetterProperty]
   20         public ITestTransient TransientProperty { getset; }
   21 
   22         [SetterProperty]
   23         public ITestSingleton SingletonProperty { getset; }
   24     }

...



   26     [TestFixture]
   27     public class TestSetterInjection {
   28         [Test]
   29         public void SetterInjection() {
   30             var factory = new Container(init => {
   31                 init.For<ITestTransient>().Use<TestTransient>();
   32                 init.SetAllProperties(setter => setter.OfType<ITestTransient>());
   33 
   34                 init.For<ITestSingleton>().Singleton().Use<TestSingleton>();
   35                 init.SetAllProperties(setter => setter.OfType<ITestSingleton>());
   36             });
   37 
   38             var target = factory.GetInstance<ClassWithPropertyDependencies>();
   39             var singleton = factory.GetInstance<ITestSingleton>();
   40 
   41             Assert.IsNotNull(target.TransientProperty);
   42             Assert.IsInstanceOf<TestTransient>(target.TransientProperty);
   43             Assert.IsNotNull(target.SingletonProperty);
   44             Assert.IsInstanceOf<TestSingleton>(target.SingletonProperty);
   45             Assert.AreSame(singletontarget.SingletonProperty);
   46         }
          }

Pros / Cons:
- Requires a public constructor still for this to work with StructureMap. Error 202 is thrown if the TestMaster constructor is made internal
- Requires properties that need to be set to be public settable. If they are not they will not be populated. This pollutes the design and can lead to misuse of a class.
- Slowest for of initialisation.
+ Good level of configuration (2 lines per dependency that needs to be used globally).

Lambda / Function Builder:
This registers a function or lambda to create an object instead of simply a type (a builder pattern).

   18     public class LambdaBuilderMaster {
   19         public static LambdaBuilderMaster CreateInstance() {
   20             return new LambdaBuilderMaster {
   21                 TransientProperty = new TestTransient(),
   22                 SingletonProperty = new TestSingleton(),
   23             };
   24         }
   25 
   26         public ITestTransient TransientProperty { getprivate set; }
   27 
   28         public ITestSingleton SingletonProperty { getprivate set; }
   29     }


...


   39     [TestFixture]
   40     public class TestBuilderStrategy {
   41         [Test]
   42         public void LambdaOrFunctionBuilder() {
   43             var factory = new Container(init => init.For<LambdaBuilderMaster>().Use(LambdaBuilderMaster.CreateInstance));
   44 
   45             var target = factory.GetInstance<LambdaBuilderMaster>();
   46             var singleton = factory.GetInstance<ITestSingleton>();
   47 
   48             Assert.IsNotNull(target.TransientProperty);
   49             Assert.IsInstanceOf<TestTransient>(target.TransientProperty);
   50             Assert.IsNotNull(target.SingletonProperty);
   51             Assert.IsInstanceOf<TestSingleton>(target.SingletonProperty);
   52             Assert.AreSame(singletontarget.SingletonProperty);
   53         }
          }

The lambdas in this example can of course point to a function elsewhere in the application and the private accessory in LambdaBuilderMaster could still be used for testing.

Pros / Cons:
+ Does not require a public constructor.
+ Does not require inappropriate properties to be settable.
+ Best performance.
- Requires a great deal of registration (1 for every type used with the IoC factory).
- Build process adds complexity to code.

Performance:
Here is a test that compares different creation methods, not so surprising results.


   44         [Test]
   45         public void Performance() {
   46             const int CreateThisMany = 10000;
   47             long time;
   48             var results = new List<KeyValuePair<stringlong>>(3);
   49 
   50             // StructureMap Setter Injection - requires a lot of configuration. 2 registrations for every property type that needs to be auto-filled.
   51             time = FactoryPerformanceTest(
   52                     new Container(config => {
   53                         config.For<ITestSingleton>().Use<TestSingleton>();
   54                         config.For<ITestTransient>().Use<TestTransient>();
   55                         config.SetAllProperties(setter => setter.OfType<ITestSingleton>());
   56                         config.SetAllProperties(setter => setter.OfType<ITestTransient>());
   57                     }),
   58                 CreateThisMany,
   59                 factory => factory.GetInstance<ClassWithPropertyDependencies>());
   60             results.Add(new KeyValuePair<stringlong>("Setter Injection"time));
   61 
   62             // Control speed benchmark using "new"
   63             time = FactoryPerformanceTest(nullCreateThisManyfactory => new ClassWithPropertyDependencies {
   64                 TransientProperty = new TestTransient(),
   65                 SingletonProperty = new TestSingleton(),
   66             });
   67             results.Add(new KeyValuePair<stringlong>("New Operator"time));
   68 
   69             // StructureMap Constructor Injection - requires no config to auto populate constructor arguments. Only 1 registration is needed per argument type globally.
   70             time = FactoryPerformanceTest(
   71                 new Container(config => {
   72                     config.For<ITestSingleton>().Use<TestSingleton>();
   73                     config.For<ITestTransient>().Use<TestTransient>();
   74                 }),
   75                 CreateThisMany,
   76                 factory => factory.GetInstance<TestConstructor>());
   77             results.Add(new KeyValuePair<stringlong>("Constructor Injection"time));
   78 
   79             // StructureMap using builder functions/lambdas - will require a registration for every type that has dependencies - probably the most configuration.
   80             var builderFactory = new Container(
   81                 config => {
   82                     config.For<ITestSingleton>().Use<TestSingleton>();
   83                     config.For<ITestTransient>().Use<TestTransient>();
   84                 });
   85             builderFactory.Configure(
   86                 config => config.For<TestConstructor>().Use(
   87                     () => new TestConstructor(builderFactory.GetInstance<ITestSingleton>(), builderFactory.GetInstance<ITestTransient>())
   88             ));
   89             time = FactoryPerformanceTest(
   90                 builderFactory,
   91                 CreateThisMany,
   92                 factory => factory.GetInstance<TestConstructor>());
   93             results.Add(new KeyValuePair<stringlong>("Function/Lambda builders"time));
   94 
   95             // AND THE WINNER IS...
   96             results.ForEach(result => System.Diagnostics.Debug.WriteLine("{0} = {1}ms"result.Keyresult.Value));
   97         }
And my output:
***** IoCComparison.StructureMapPerformanceTest.StructureMapPerformance
Setter Injection = 200ms
New Operator = 1ms
Constructor Injection = 69ms
Function/Lambda builders = 20ms



***** IoCComparison.NinjectPerformanceTest.NinjectPerformance
Setter Injection = 594ms
New Operator = 2ms
Constructor Injection = 516ms
Function/Lambda builders = 521ms


Structure Map versus Ninject
Its clear to see based on the performance results above that the same usage scenarios through both frameworks that StructureMap is the clear winner.  The only downside I can see to StructureMap is that there is no support for Silverlight (Client Framework)... yet.  A recent post on Stack Overflow points to July/August 2010 for a Silverlight release of StructureMap.  I do however really like Ninject's mission statement to keep their framework simple. In terms of quality of documentation and widespread adoption, again, StructureMap seems to be the clear winner.

My Recommendation on IoC Usage:
  1. If your class is intended to be public createable then use Constructor Injection. This is less configuration and it exhibits auto-detect dependencies capability; meaning if someone adds a new parameter to the constructor, the factory may be able to populate it without a new registration.
  2. If a class is not intended to be public createable then use the Lambda / Function Builder strategy.  In addition it may sometimes be cleaner code to use this over constructor injection.  Having said that, if you have a constructor with many arguments it could indicate the class is in need of a refactor.
  3. Only in special cases use Setter Injection where there are very strong reasons that the above two methods are not appropriate. That means more than "I don't like the above two".
Service Locator Pattern (anti-pattern?)
IoC factories are quite often used as a Service Locator pattern.  This means a singleton instance is available globally throughout the application via a static class and property. For example:

    9     public static class ObjectFactory {
   10         public static IContainer Container get; }
   11     }

This isn't overly bad until you consider if your code is sprinkled with this call all over the place, creating a tight dependency on a particular IoC and one instance of it.


19             var service = ObjectFactory.Container.GetInstance<IService>();



On the up side it minimises all other coupling quite nicely. So instead of having tight coupling with all components you only have tight coupling with one.

One major downside is unit testing.  Consider if you have hundreds of tests, with some running simultaneously, each trying to configure and use the static ObjectContainer all at once... not going to work well.

There is no easy way to completely eliminate these service locator calls. The key thing is to minimise the use of a static ServiceLocator call.

My simple strategy is to use a protected or private property of type IContainer. 


   23     public class Service : IService {
   24         public Service() : base() {
   25         }
   26 
   27         private IContainer factory = null;
   28         protected IContainer Factory {
   29             get {
   30                 if (this.factory == null) {
   31                     this.factory = ObjectFactory.Container;
   32                 }
   33 
   34                 return this.factory;
   35             }
   36 
   37             set {
   38                 this.factory = value;
   39             }
   40         }
   41     }

Generally, avoiding static references to the factory require passing the factory around, typically through the constructor.  The above strategy means that a lot of classes are not polluted with IContainer in their constructors.  It also means that you do not need to pass IContainer into a class just so it can pass it to another internally created class that needs it.  The whole idea of avoiding calls to ObjectFactory.Container is to allow testing and prevent cross-pollution issues with running parallel tests (at least thats where I always find Service Locator issues).  

To test the above example class, the test can instantiate it and then set the Factory property using a private accessor strategy (I use reflection to set these in tests, MsTest has a great private accessor feature).  

   70         [Test]
   71         public void TestService() {
   72             var target = new Service();
   73             var testFactory = new StructureMap.Container();
   74             PrivateAccessor.SetProperty(target"Factory"testFactory);
   75             var result = target.DoSomething();
   76             // ... Test outcome etc
   77         }
There are a few ways to set private properties, lets not go into that now, suffice to say, my PrivateAccessor is simply using reflection to get the property and set it. But the above test works fine and will not compete with other tests for a shared static resource.


Production code does not need to concern itself with any special treatment of the class, and this is as it should be; test code should not pollute classes. The only caveat is that you cannot use the Factory property inside the constructor (this can be avoided with lazy loading of properties the same way the Factory property works).
...

   78             var service = new Service();
   79             var data = service.DoSomething();
...

As you can see nice clean production code that requires no special treatment to allow unit testing.

Hope that helps someone.
Cheers

1 comment:

  1. Here's a great post on returning conditional instances.

    http://codebetter.com/jeremymiller/2009/01/19/conditional-object-construction-in-structuremap-i-e-fun-with-lambdas/

    ReplyDelete