Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Setter Injection in StructureMap 2.5

I know this isn’t the most exciting post I’ve ever written, but I’m blogging the new StructureMap 2.5 documentation as I create it.  I’ve done quite a bit of work in StructureMap 2.5 to extend the support for Setter Injection and it’s worthy of some attention for new and old StructureMap users.  *I* still think that Setter Injection is sub-optimal, but there are times when it’s necessary.  I’ll make a separate post later on how some of the newer setter injection support can be used to create an equivalent to Windsor’s logging facility.

 

Setter Injection

StructureMap supports two forms of Dependency Injection:

  1. Constructor Injection -- "Pushing" dependencies into a concrete class through constructor arguments.
  2. Setter Injection -- "Pushing" dependencies into a concrete class through public properties.  The "Setter" nomenclature is taken from Java where properties are getSomething() and setSomething(value).

You can certainly mix and match Setter Injection with Constructor Injection on the same classes, but Constructor Injection will always be used (except for empty constructors) and Setter Injection has to be explicitly configured.  See Martin Fowler's discussion on Constructor versus Setter Injection for more information.  My feeling has always been that Constructor Injection is preferrable from a design perspective.  When you exclusively use Constructor Injection, the code is somewhat more self-documenting because the constructor arguments will clearly delineate the dependencies of a concrete class.  It's also important to think about the constructor method of a class being a contract.  If you satisfy all of the arguments of the constructor method, the class should be ready to function.  Relying on Setter Injection can make a class harder to use because it isn't always obvious which setters need to be created externally to use the class.  Of course, not using any form of Dependency Injection can be the worst answer because then you have no idea what it really takes to bootstrap the service.

Despite my personal distaste for Setter Injection, I gave into user demand and greatly increased StructureMap's support for Setter Injection -- and promptly found that support to be more useful than I thought it would be.

 

Using Setter Injection

Setter injection is a pattern of "injecting" dependencies via public properties.  Setter Injection with StructureMap is somewhat a second class citizen, but this is partially by design.  My strong recommendation is to use constructor injection for all code that you control, and save setter injection strictly for classes from external libraries where you do not have any control.  As a summary, these are the Setter Injection features that are described in detail below:

  • By default, all public "Setters" are optional, meaning that these setters will only be set if they are explicitly configured for a specific Instance
  • StructureMap can be directed to "auto wire" a property for a given Instance
  • Setters can be made mandatory by decorating the property with the [SetterProperty] attribute
  • Any type that StructureMap can use in constructor arguments can be used for setter properties
  • StructureMap can be directed to automatically inject the default value for any property of a given type.  This was added primarily for "optional" dependencies like logging.
  • The StructureMap diagnostic tools will check for missing Setter values

 

Configuring Primitive Setter Properties

Ok, now that you've read that I don't believe that setter injection is a good idea, but you're bound and determined to use it anyway, let's talk about how to do it with StructureMap.  Let's say we have this class with a string property called "Name":

    public class OptionalSetterTarget

    {

        public string Name { get; set;; }

    }

I can specify the value of the "Name" property in the configuration API like this:

        [Test]/p>

        public void optional_setter_injection_with_string()

        {

            var container = new Container(r =>

            {

                // The "Name" property is not configured for this instance

                r.InstanceOf<OptionalSetterTarget>().Is.OfConcreteType<OptionalSetterTarget>().WithName("NoName");

 

                // The "Name" property is configured for this instance

                r.ForConcreteType<OptionalSetterTarget>().Configure

                    .WithProperty("Name").EqualTo("Jeremy");

            });

 

            container.GetInstance<OptionalSetterTarget>().Name.ShouldEqual("Jeremy");

            container.GetInstance<OptionalSetterTarget>("NoName").Name.ShouldBeNull();

&        }

In the case above I specified the value for the "Name" setter directly by embedding the property value directly with the WithProperty("Name").EqualTo("Jeremy").  You could also retrieve the value for the property from the AppSettings portion of a .Net application config file like this with the WithProperty("Name").EqualToAppSetting("name") syntax.

            var container = new Container(r =>

            {

                r.ForConcreteType<OptionalSetterTarget>().Configure

                    .WithProperty("Name").EqualToAppSetting("name");

            });

In both of the cases above the property name is designated by a string.  Using strings to designate property names is always going to be somewhat problematic, so there's a new option in 2.5 to specify property values with a Lambda expression (inspired by this post from Udi Dahan) using an Action<T>.

                r.ForConcreteType<OptionalSetterTarget>().Configure

                    .SetProperty(x => x.Name = "Jeremy");

The value of the Lamdba expression mechanism is that it makes the configuration static-typed with all of the advantages that static typing brings.  This action is executed on the object after creation.  Technically, this mechanism can be used to do anything to the new object before StructureMap returns the new object to the code that requested it.  There is no limitation to the number of Action<T> handlers you can use.

 

Configuring Setter Dependencies

Let's say you have a class that has a public property for a singular dependency and another public property for an array of dependencies.

    public class ClassWithDependency

    {

        public Rule Rule { get; set; }

        public Rule[] Rules { get; set; }

    }

I can explicitly configure what gets injected into the "Rule" property for a specific Instance of ClassWithDependency like this with the .SetterDependency<T>() method that will look for the first public setter of type T:

                r.ForConcreteType<ClassWithDependency>().Configure

                    .SetterDependency<Rule>().Is(new ColorRule("Red"));

or for cases where a class may have multiple public setters of the same type, you can specify the exact property with an Expression (.SetterDependency<T>( expression ) ):

            var container = new Container(r =>

            {

                r.ForConcreteType<ClassWithDependency>().Configure

                    .SetterDependency<Rule>(x => x.Rule).Is(new ColorRule("Red"));

            });

 

"Auto Filling" a Setter Dependency

Sometimes all you want to do is to simply say "fill in this property for me."  That's what the code below does for the "Rule" property with the .SetterDependency<Rule>().IsTheDefault() syntax:

            var container = new Container(r =>

            {

                r.ForConcreteType<ClassWithDependency>().Configure

                    .SetterDependency<Rule>().IsTheDefault();

 

                r.ForRequestedType<Rule>().TheDefault.Is.Object(new ColorRule("Green"));

            });

   

For certain dependency types you might want StructureMap to automatically fill any public property of that type.  The first usage that comes to mind is logging.  Let's say that we have an interface for our logging support called ILogger.  We can specify that any concrete class that has a public property for the ILogger type will be filled in construction with code like this (FillAllPropertiesOfType<ILogger>()):

            var container = new Container(r =>

            {

                r.FillAllPropertiesOfType<ILogger>().TheDefault.Is

                    .ConstructedBy(context => new Logger(context.ParentType));

            });

Now, if I have some classes like:

    public class ClassWithLogger

    {

        public ILogger Logger { get; set; }

    }

 

    public class ClassWithLogger2

    {

        public ILogger Logger { get; set; }

    }

Now, when StructureMap builds new instances of these classes above the Logger properties will be filled automatically without any explicit configuration for either type.  Here's a sample from the unit tests that constructs objects of both ClassWithLogger and ClassWithLogger2 and verifies that the "Logger" property was filled for both types without any further configuration.

 

            container.GetInstance<ClassWithLogger>().Logger.ShouldBeOfType<Logger>();

            container.GetInstance<ClassWithLogger2>().Logger.ShouldBeOfType<Logger>();

 

 

 

Defining Setter Properties with Attributes

Just use the [StructureMap.Attributes.SetterProperty] to denote properties that need to be filled by StructureMap.  Marking a property with the [SetterProperty] makes the setter mandatory.  StructureMap will throw an exception if the "ShouldCache" property isn't specified for the concrete type shown below.  If the "Provider" property isn't explicitly configured, StructureMap will use the default instance of IDataProvider for the "Provider" property (or throw an exception if StructureMap doesn't know how to build the type IDataProvider).

    public class Repository

    {

        private IDataProvider _provider;

 

        // Adding the SetterProperty to a setter directs

        // StructureMap to use this property when

        // constructing a Repository instance

        [SetterProperty]/p>

        public IDataProvider Provider

        {

            set

            {

                _provider = value;

            }

        }

 

        [SetterProperty]

        public bool ShouldCache { get; set; }

    }

 

Defining Setter Properties in Xml

Setter properties can be defined in the Xml configuration by explicitly directing StructureMap to use setter properties while building a concrete type.  In the Xml, Setter configuration is done with the exact syntax as constructor arguments.  For example, the "Name" property of the OptionalSetterTarget class shown in previous sections can be set just like a constructor argument in an Xml node:

  <StructureMap MementoStyle="Attribute"

    <DefaultInstance

        PluginType="StructureMap.Testing.Pipeline.OptionalSetterTarget, StructureMap.Testing"

        PluggedType="StructureMap.Testing.Pipeline.OptionalSetterTarget, StructureMap.Testing"

        Name="Jeremy" />

  </StructureMap>

 

Setter properties can also be designated as mandatory setters with the <Setter> node in the Xml configuration.  From the unit tests, I have a class called OtherGridColumn that exposes several properties:

    public class OtherGridColumn : IGridColumn

    {

        public IWidget Widget { get; set; }

 

        public string ReadOnly

        {

            get { return "whatever"; }

        }

 

        public FontStyleEnum FontStyle { get; set; }

        public string ColumnName { get; set; }

        public Rule[] Rules { get; set; }

        public bool WrapLines { get; set; }

        public bool Displayed { get; set; }

        public int Size { get; set; }

    }

 

I can direct StructureMap to make the properties on the OtherGridColumn class mandatory in a <Plugin> node for OtherGridColumn.  This probably isn't necessary with the new optional setter injection capabilities, but it is still valid and the equivalent of using the [SetterProperty] attribute.

  <PluginFamily/span> Type="StructureMap.Testing.Widget5.IGridColumn" Assembly="StructureMap.Testing.Widget5" DefaultKey="">

    <Plugin Assembly="StructureMap.Testing.Widget5" Type="StructureMap.Testing.Widget5.OtherGridColumn" ConcreteKey="Other">

      <Setter Name="ColumnName" />

      <Setter Name="FontStyle" />

      <Setter Name="Rules" />

      <Setter Name="Widget" />

      <Setter Name="WrapLines" />

    </Plugin>

  </PluginFamily&>


Posted Wed, Oct 8 2008 11:26 PM by Jeremy D. Miller

[Advertisement]

Comments

Reflective Perspective - Chris Alcock » The Morning Brew #197 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #197
on Thu, Oct 9 2008 3:19 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #197

Scott Parker wrote re: Setter Injection in StructureMap 2.5
on Thu, Oct 9 2008 9:43 AM

Not one of the most exciting posts ever? Well, Setter Injection happens to be a hot topic at my place of work so I must differ on that point.

Am I correct in saying this isn't available in the 2.4.9 preview (or at least the FillAllPropertiesOfType method)? I agree that this isn't perhaps the most ideal setup, but handling our Setters was a serious point of contention in getting StructureMap adopted here in the office.

Keep up the good work!

-Scott

Jeremy D. Miller wrote re: Setter Injection in StructureMap 2.5
on Thu, Oct 9 2008 9:48 AM

@Scott,

You are correct.  All the new fancy functionality for setter injection is NOT in 2.4.9.  One of the reasons that the 2.5 release has been delayed so long has been the number of functionality requests that have rolled out in the StructureMap board.

Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew
on Thu, Oct 9 2008 9:58 AM

Pingback from  Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew

Wire Setters roundup » Quick scan of the net - wire setter wrote Wire Setters roundup &raquo; Quick scan of the net - wire setter
on Thu, Oct 9 2008 10:21 AM

Pingback from  Wire Setters roundup &raquo; Quick scan of the net - wire setter

J. Ruiz wrote re: Setter Injection in StructureMap 2.5
on Thu, Oct 9 2008 7:21 PM

Jeremy,

I favor constructor injection for all the reasons you state, but there are two scenarios where I favor setter injection and I wanted to get your opinion on those (I've only been developing software for 5 years).

First, I may wish to re-inject a new dependency implementation dynamically.

Second is for clarity.  Let's say I have an object that requires an interface implementation that provides some infrastructure service.  This service requires some initialization, and initialization may throw exceptions.  

I think it's a bad idea to inject this service into the constructor and call initialize there because throwing exceptions from constructors is dirty.

Of course, having to try/catch a property is less-than-clean code as well, but I feel like if I want to keep the contract of "inject these things into the constructor and that's it: you're ready to go", I need to inject the described service into a setter.  In this scenario, setter injection clearly says to me that my object is not quite ready to go.

What do you think?

Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew
on Sun, Oct 12 2008 6:02 PM

Pingback from  Dew Drop - October 9, 2008 | Alvin Ashcraft's Morning Dew

Brian wrote re: Setter Injection in StructureMap 2.5
on Mon, Nov 10 2008 4:09 PM

Can you give an example of using the below to set the property via code using VB?  I can't figure out how you would do it.

           var container = new Container(r =>

           {

               r.FillAllPropertiesOfType<ILogger>().TheDefault.Is

                   .ConstructedBy(context => new Logger(context.ParentType));

           });

Jeremy D. Miller wrote re: Setter Injection in StructureMap 2.5
on Mon, Nov 10 2008 4:36 PM

@Brian,

Let's just say that SM is accidentally optimized for C#.

In VB.Net the consensus seems to be to make a method of signature:

SetUpMyContainer(x as InitializationExpression)

   x.Fill.............

End

and pass that method as a delegate to the Container ctor.

Step 2.)  Bitch like hell to the VB.Net team for doing such a bad job on Lambda expressions.  You really need multi-line Lambda expressions.

Brian Johnston wrote re: Setter Injection in StructureMap 2.5
on Tue, Nov 11 2008 8:15 AM

Thank Jeremy - yes, anytime I work in VB instead of C# I stomp my feet and whine like a 6 year old...I was hoping maybe you knew a trick I wasn't aware of since I haven't done VB since 2.0, but it sounds like your work-around is the same as mine.  <sigh>

Jeremy D. Miller -- The Shade Tree Developer wrote A Gentle Quickstart for StructureMap 2.5
on Sun, Nov 30 2008 10:56 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

Community Blogs wrote A Gentle Quickstart for StructureMap 2.5
on Sun, Nov 30 2008 11:48 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Tue, Dec 2 2008 10:16 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Wed, Dec 3 2008 11:04 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Thu, Dec 4 2008 11:09 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

DoniG wrote re: Setter Injection in StructureMap 2.5
on Tue, Dec 16 2008 10:44 AM

As a C# programmer trying to configure StructureMap in VB.Net, I can tell you that throwing a few bones to the poor VB.Net guys via some example code would be greatly appreciated!

I never understood how badly done VB.Net's lambda support was until I attempted this...what is trivial to do in C# is near impossible in VB.Net.

Jeremy D. Miller wrote re: Setter Injection in StructureMap 2.5
on Tue, Dec 16 2008 9:17 PM

@DoniG,

Heard and understood.  I've recruited Rod Paddock to help me with some VB.Net samples.  I'm giving some thought to making SM a bit more VB friendly in the next pass.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us