CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

“SmartInstance” in StructureMap 2.5

From the feedback on the StructureMap Google group, Chad and I have hashed out a new Fluent Interface expressions for explicitly defining constructor arguments and setter values (I still think Setter Injection is a design smell, but I’ve been vociferously voted down).  The problems in the current version is that SetProperty() is overloaded to mean either setter or constructor arguments.  The underlying mechanisms in StructureMap stores the information the same way, but the API was causing real confusion.  So, to alleviate that confusion and also to utilize some of the new .Net 3.5 goodness, I present the new language:

Defining primitive constructor arguments -- WithCtorArg(“name”).EqualTo(“value”)  or  WithCtorArg(“name”).EqualToAppSetting(“key”)

        [Test]

        public void DeepInstanceTest_with_SmartInstance()

        {

            assertThingMatches(registry =>

            {

                registry.ForRequestedType<Thing>().TheDefault.Is.OfConcreteType<Thing>()

                    .WithCtorArg("name").EqualTo("Jeremy")

                    .WithCtorArg("count").EqualTo(4)

                    .WithCtorArg("average").EqualTo(.333)

                    .SetterDependency<Rule>().Is(x =>

                    {

                        x.OfConcreteType<WidgetRule>().SetterDependency<IWidget>().Is(

                            c => c.OfConcreteType<ColorWidget>().WithCtorArg("color").EqualTo("yellow"));

                    });

            });

        }

 

Defining primitive setter properties – just uses a Lambda expression that will be applied to the object as soon as it’s built.  Intellisense and compiler safety are good things, so you might as well use it.  StructureMap now supports optional setter injection, meaning that you no longer need to do the [Setter] attributes in the concrete classes.  If you specify the value of a setter, StructureMap will use that value regardless of whether or not the [Setter] property exists.  The same rule applies to non-primitive setter dependencies.

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

 

Overriding Constructor Dependencies – Only when you want to override the auto wiring behavior.  I’ve chosen to use a Nested Closure for defining the child instance of the IWidget constructor argument below.  You could also replace x.Object() with x.OfConcreteType<T> or x.ConstructedBy(Func<T>) or other options inside the Is() method.  I chose this solution because I thought it would make it easier to guide the user to the possible options.

instance.CtorDependency<IWidget>("widget").Is(x => x.Object(widget))

 

Overriding Setter Dependencies – Setter dependencies (non-primitive types) are specified much like constructor arguments.  Your options are to say:  .SetterDependency<T>().Is(whatever) or .SetterDependency<T>(x => x.Setter).Is(whatever).

                registry.ForRequestedType<Thing>().TheDefault.Is.OfConcreteType<Thing>()

                    .WithCtorArg("name").EqualTo("Jeremy")

                    .WithCtorArg("count").EqualTo(4)

                    .WithCtorArg("average").EqualTo(.333)

                    .SetterDependency<Rule>().Is(x =>

                    {

                        x.OfConcreteType<WidgetRule>().SetterDependency<IWidget>().Is(

                            c => c.OfConcreteType<ColorWidget>().WithCtorArg("color").EqualTo("yellow"));

                    });

 

Explicitly defining an array of dependencies – I get into this scenario with configuring business rules.  Let’s say you have a class that depends on an array of some other type of service.  That syntax looks like:

                registry.ForRequestedType<Processor>().TheDefault.Is.OfConcreteType<Processor>()

                    .WithCtorArg("name").EqualTo("Jeremy")

                    .TheArrayOf<IHandler>().Contains(x =>

                    {

                        x.References("Two");

                        x.References("One");

                    });

 

or

 

            IContainer container = new Container(r =>

            {

                r.ForRequestedType<Processor>().TheDefault.Is.OfConcreteType<Processor>()

                    .WithCtorArg("name").EqualTo("Jeremy")

                    .TheArrayOf<IHandler>().Contains(x =>

                    {

                        x.OfConcreteType<Handler1>();

                        x.OfConcreteType<Handler2>();

                        x.OfConcreteType<Handler3>();

                    });

            });

 

In this case, I was lazy and made no distinction between constructor arguments and setter arguments.

 

 

Adding additional instances of a given type – Sometimes you’re adding more than one instance of a given service type to StructureMap.  You may be fetching by ObjectFactory.GetNamedInstance<T>(name) or by ObjectFactory.GetAllInstances<T>().  Either way, this syntax will work.

                registry.ForRequestedType<IService>().AddInstances(x =>

                {

                    x.OfConcreteType<ColorService>().WithName("Red")

                        .WithCtorArg("color").EqualTo("Red");

 

                    x.Object(new ColorService("Yellow")).WithName("Yellow");

 

                    x.ConstructedBy(() => new ColorService("Purple")).WithName("Purple");

 

                    x.OfConcreteType<ColorService>().WithName("Decorated").WithCtorArg("color").EqualTo(

                        "Orange");

                }));

 

 

Thoughts?  Comments?  I should say here that StructureMap 2.5 will be about 95% backwards compatible with the existing FI, so no worries about converting.

 

 

 

It’s funny, but I generally think that with few exceptions the Constructor Injection is the preferable approach, but I continuously read that the Java guys are exactly the opposite.  To each his or her own I guess.



Comments

Scott said:

I agree on setter injection usually, but I will say that the WithCtorArg is much more indicative of the intent, so at the very least, thank you for the name change :) (or add i guess)

WithProperty was confusing because when you first see it you assume that it means "do setter injection here".  Well, I assumed that anyway.

# August 20, 2008 10:13 PM

Jeremy D. Miller said:

Live and learn Scott.  This has been a good learning experience for me in terms of learning how to create better API's.  

On the other hand, I had a colleague one time that used to say "oh no, here comes another learning opportunity"

# August 20, 2008 11:39 PM

Steve Bohlen said:

I just have to chime in here to ask a question (not a flame, a real question): Is there any such thing as TOO verbose a fluent interface?

I'm (generally) a fan of mostly-fluent interfaces for a number of things, but IMHO this is starting to get gratuitious to the point of actually DIMINISHING readability rather than improving it.  As the 'quest-for-sentence-like-structures' starts to introduce pronouns, prepositions, and other similar spoken/written language constructs, the signal to noise ratio in many FI implementations starts to just hurt my brain :)

Clearly your opinion is that the above level of 'fluency' is desirable, but I wonder in your own opinion if you would consider (as a design philosophy) that there is such a thing as too fluent an interface?

Is the goal REALLY to be able to say When.I.have.a.type.called.this.please.create.a.concrete.instance.of.this.type.unless.the.constructor.contains.an.argument.with.a.value.of.2.in.which.case.return.this.other.type().

Its not clear to me that this is either attainable OR desirable unless the plan is for a business analysts to read (and maintain) it after I am dead and buried.

Thoughts?

# August 21, 2008 6:44 AM

Jeremy D. Miller said:

@Steve,

I don't particularly disagree with anything you said.  If the "fluency" is doing more harm than good in terms of readability and effort, then yeah, it's a bad thing.

Truth be told, the "TheDefault.Is.Blah" syntax has somewhat to do with me being able to reuse a certain piece of code in the expression builders than purely a regard for readability.

# August 21, 2008 8:32 AM

Dew Drop - August 21, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - August 21, 2008 | Alvin Ashcraft's Morning Dew

# August 21, 2008 10:10 AM

Scott said:

@Steve I agree wholeheartedly with the premise of your comment.  It can get very easy to abuse the FI and reduce the solubility of the code.

However, I find the one here in SM largely understandable and easy to work with. The constraints of the language/platform have always led us down paths we don't necessarily like all the time. It's the nature of the beast.

@Jeremy Don't take my initial comment as anything other than face value. I have a hard enough time getting API design right for consumption by an internal small team. I don't at all envy your job (with structuremap) of creating an API that works for a wide variety of consumers.  Keep up the great work brotha.

# August 22, 2008 10:12 AM

RhysC said:

"Java guys are exactly the opposite."

It funny you say that becuase i have java buddies that seem to disagree in terms of using annotations/attributes over extrenal mapping or configs too, specifically in hibernate, but i assume this goes for other potentiall invasive framework libraries. Weird.

Anyway Jeremy i am with you, i certainly prefer ctor injection over setter, but hey as long as everyone is happy. Good Job.

# August 28, 2008 11:42 AM

Recent Faves Tagged With "intellisense" : MyNetFaves said:

Pingback from  Recent Faves Tagged With "intellisense" : MyNetFaves

# October 15, 2008 11:50 PM

Jeremy D. Miller -- The Shade Tree Developer said:

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

# November 30, 2008 10:56 PM

Community Blogs said:

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

# November 30, 2008 11:48 PM

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

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

# December 2, 2008 10:16 AM

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

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

# December 3, 2008 11:04 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With


This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP