Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

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
"BuildUp" Existing Objects with StructureMap

I know, you're plrobably sick of me clogging the CodeBetter feed with this stuff, but I needed to post this just to answer a question on the StructureMap list.  A longstanding feature request is to be able to apply setter injection of dependencies into an object that's already constructed.  The main culprit is WebForms and other *ahem* frameworks that don't allow you to control the construction of objects.  I resisted it for quite some time because a.) I don't do WebForms and b.) I thought it was going to be hard. Well, enought people asked for it, I found a very good reason to use it on our app, and it turned out that it only took an hour or so to implement*, so here it is (excerpted from the StructureMap docs):

Many times you simply cannot control when an object is going to be created (ASP.Net WebForms), but you may still want to inject dependencies and even primitive values into an already constructed object.  To fill this gap, StructureMap 2.5.2+ introduces the "BuildUp()" method on Container and ObjectFactory.  BuildUp() works by finding the default Instance for the concrete type passed into the BuildUp() method (or create a new Instance if one does not already exist), then applying any setters from that Instance configuration.  At this time, StructureMap does not apply interception inside of BuildUp().

Let's say that we have a class called "BuildTarget1" like this:

    public class BuildUpTarget1

    {

        public IGateway Gateway { get; set; }

    }

In usage, we'd like to have the Gateway dependency injected into a new instance of the BuildTarget1 class when we call BuildUp():

        [Test]

        public void create_a_setter_rule_and_see_it_applied_in_BuildUp_through_ObjectFactory()

        {

            var theGateway = new DefaultGateway();

            ObjectFactory.Initialize(x =>

            {

                x.ForRequestedType<IGateway>().TheDefault.IsThis(theGateway);

 

                // First we create a new Setter Injection Policy that

                // forces StructureMap to inject all public properties

                // where the PropertyType is IGateway

                x.SetAllProperties(y =>

                {

                    y.OfType<IGateway>();

                });

            });

 

            // Create an instance of BuildUpTarget1

            var target = new BuildUpTarget1();

 

            // Now, call BuildUp() on target, and

            // we should see the Gateway property assigned

            ObjectFactory.BuildUp(target);

 

            target.Gateway.ShouldBeTheSameAs(theGateway);

        }

BuildUp() also works with primitive properties (but I'm not sure how useful this will really be):

    public class ClassThatHasConnection

    {

        public string ConnectionString { get; set; }

    }

 

    [TestFixture]

    public class demo_the_BuildUp

    {

        [Test]

        public void push_in_a_string_property()

        {

            // There is a limitation to this.  As of StructureMap 2.5.2,

            // you can only use the .WithProperty().EqualTo() syntax

            // for BuildUp()

            // SetProperty() will not work at this time.

            var container = new Container(x =>

            {

                x.ForConcreteType<ClassThatHasConnection>().Configure

                    .WithProperty(o => o.ConnectionString).EqualTo("connect1");

 

            });

 

            var @class = new ClassThatHasConnection();

            container.BuildUp(@class);

 

            @class.ConnectionString.ShouldEqual("connect1");

        }

    }

 

How my Team Uses This

I mentioned earlier that I finally did this because I wanted to use it for my project.  We extensively use stuff in our views like:

    var dashboardUrl = '<%= this.ActionUrl<HomeController>(c => c.DashboardIndex()) %>'

    var consoleUrl = '<%= this.ActionUrl<HomeController>(c => c.Console(null)) %>'

That call in the view to ActionUrl<T>() calls into a Dovetail specific class called IUrlRegistry that "knows" what the actual url is for each controller method.  Great, but that means my views need to get at the IUrlRegistry singleton.  We started to get ObjectFactory.GetInstance<IUrlRegistry>() calls sprinkled into the base class for our views, but that's an anti pattern.  Instead, we now use the BuildUp() function to inject a couple common services into our views.  First, I created some properties on the View base page called "Urls" and "Container":

    public class DovetailViewPage<VIEWMODEL> : ViewPage<VIEWMODEL>, ITestablePage, IDovetailViewWithModel<VIEWMODEL>

        where VIEWMODEL : ViewModel

    {

        public DovetailViewPage()

        {

            ObjectFactory.BuildUp(this);

        }

 

        public IUrlRegistry Urls { get; set; }

        public IContainer Container { get; set; }

    }

We didn't find a clean way to intercept the creation of WebForms objects in the MVC framework without rewriting the WebFormsEngine class completely (we're in contact with the MVC team trying to get this relaxed a bit and get a new seam put in there), so I punted and just made a quick call to ObjectFactory.BuildUp(this) in the constructor function to force StructureMap to push in the two setters.  It's not ideal, but it's good enough for the moment. 

Lastly, how does StructureMap know to set those two properties, and not others?  That's where the new setter injection policies come into play.  Inside the Registry that we use to bootstrap our MVC application we have a setter policy that just says "always inject IUrlRegistry and IContainer":

    public class WebCoreRegistry : Registry

    {

        public WebCoreRegistry()

        {

            SetAllProperties(x =>

            {

                x.OfType<IUrlRegistry>();

                x.OfType<IContainer>();

            });


        }

    }

The "IContainer" property is just a reference to the current Container object behind ObjectFactory.

 

 


* StructureMap uses Reflection.Emit to write out the "InstanceBuilder" classes that invoke constructor functions and apply setter injection to concrete classes.  I did it that way partially to gain experience with IL generation for another project I ended up abandoning.  Frankly, emitting IL is a humongous PITA.  StructureMap 2.5 represents a near rewrite, but I left the emitting code mostly alone with just some minor cleanup.  When .Net 4.0 hits with the ability to do much more with Expressions, I'd at least like to try to replace the emitting code with Expression munging.


Posted Fri, Jan 16 2009 9:41 AM by Jeremy D. Miller
Filed under:

[Advertisement]

Comments

ASP.NET MVC Archived Blog Posts, Page 1 wrote ASP.NET MVC Archived Blog Posts, Page 1
on Fri, Jan 16 2009 10:23 AM

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

Christiaan Baes wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 10:36 AM

Cool. This will come in handy to inject stuff in winforms usercontrols and keep the designer happy and usable at the same time.

Perhaps I should read the rest of the FM.

Jeremy D. Miller wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 10:45 AM

The "FM" is less than a week old, so I'll make some allowances ;)

Travis wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 11:49 AM

Thank you for this. So the "setter injection policies" only come in to play when BuildUp() is called?

Jeremy D. Miller wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 11:54 AM

@Travis,

No, the setter injection policies are global, and act in all cases.  It's just that the BuildUp() will be much more useful with the combination of BuildUp + Setter Injection Policies.

Or at least that's my theory...

Jimmy Bogard wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 12:00 PM

Thanks for this.  Otherwise, we had a bunch of ObjectFactory.GetInstance calls inside our base view page class, manually setting all of the fields.  That was fugly.

Paco wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 12:16 PM

Good post serie about StructureMap!

A bit offtopic, but I wonder how you did build the IUrlRepository implementation. Does it use caching or does it use Lamba.Compile?

I'm asking this because Html.ActionLink is Asp.Net mvc is the main bottleneck in the app I'm working on.

chrissie1 wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 4:02 PM

Thank you again for all your hard work. I made a little post about this feature. blogs.lessthandot.com/.../structuremap-is-way-cool . I will sacrifice an animal in your name tommorow (for the animal lovers out there, an ant or a fly nothing major).

Jeremy D. Miller wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 4:22 PM

@Paco:

We haven't hit a performance problem on that, but we haven't done any load testing yet.  You're scaring me a little bit.

Chad's idea for that is to have the url's read into constants right after app initialization.  That'd largely remove that problem.

Jeremy D. Miller wrote re: "BuildUp" Existing Objects with StructureMap
on Fri, Jan 16 2009 4:23 PM

@chrissie,

Thank you for the comment *and* the blog post.

chrissie1 wrote re: "BuildUp" Existing Objects with StructureMap
on Sat, Jan 17 2009 5:03 AM

And now there is also a blogpost where I use VB.Net.

blogs.lessthandot.com/.../structuremap-is-way-cool-even-in-vb-net

DotNetShoutout wrote "BuildUp" Existing Objects with StructureMap
on Sun, Jan 18 2009 11:32 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

IoC - StructureMap Links « QuantuMatrix’s Weblog wrote IoC - StructureMap Links &laquo; QuantuMatrix&#8217;s Weblog
on Mon, Jan 19 2009 12:16 PM

Pingback from  IoC - StructureMap Links &laquo; QuantuMatrix&#8217;s Weblog

Kazi Manzur Rashid's Blog wrote ASP.NET MVC Best Practices (Part 1)
on Wed, Apr 1 2009 11:44 AM

In this post, I will share some of the best practices/guideline in developing ASP.NET MVC applications

Add a Comment

(required)  
(optional)
(required)  
Remember Me?