“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.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. 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 is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/jmiller/default.aspx Jeremy D. Miller

    @Peter,

    ObjectFactory.With(my other thing).GetInstance()

  • peter

    Hi!
    a question :
    say I want to get back an instance of ISomething but to give it a value for the constructor. eg.
    public class something : ISomething{
    public something (IOtherthing other_stuff){..}
    ….}

    now in code I got something like this..
    public void do_something_with(IOtherthing thisThing) {
    var something = ObjectFactory.Get(? thisThing????)

    Is that possible? how do I configure it & use it
    }

  • Kurt Harriger

    One major advantage of setter injection over constructor injection that I have found is when the type is meant to be subclassed. With constructor injection the derived type also needs same constructor args plus whatever it needs. This isn’t necessarily so bad if the base class has only one or two required dependencies, but if you later add another dependency to the base class then you need to update the constructors of all derived types. With setter injection you do not need to change the derived types.

    I also try to provide a default implementation where possible, and prefer setter injection to override default rather than constructor making it much easier to setup container when default implementation does what you want.

  • http://rhysc.blogspot.com/ RhysC

    “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.

  • http://www.scottcreynolds.com Scott

    @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.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @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.

  • http://blog.unhandled-exceptions.com Steve Bohlen

    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?

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    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”

  • http://www.scottcreynolds.com Scott

    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.