Views: 20547
Number of votes: 6
Average rating:

An introduction to StructureMap

As some of you are aware I had the great honor of spending my 30th birthday giving a presentation about the IoC-container StructureMap to the Stockholm EPiServer meetup group. The focus of the presentation, and this blog post, was not to dwell into the subject of what IoC is or what problem it tries to solve. This has already been written about countless times and if you’re interested (which you should be!) you can take a look at (a pretty poor) wikipedia article, Martin Fowler, Joel Abrahamsson, and one (of many) StackOverflow posts.

What I’m going to focus on here is what StructureMap can do for you and how you go about doing it.

TLDR: This is a rather long post so if you’re more of a compile and run type of person scroll to the bottom and download the example solution.

 

A simple service…

Imagine that you’ve recently started to look at the SOLID principle and in particular the Dependency Inversion Principle. This might lead to a code structure that looks something like this:

sm_service

So, our service has a constructor dependency to some validation and some repository abstraction. The concrete implementation of the repository itself ha a dependency to some configuration and some logger. The loggers concrete implementation has a dependency to some other abstraction. When I say that the concrete implementation of, for instance, the repository has a dependency to configuration and logger what this means in code is this:

   1: public class Repository : IRepository
   2: {
   3:     public Repository (IConfiguration configuration, ILogger logger)
   4:     {
   5:     }
   6: }

 

Creating a new instance of our service would look like this

   1: new Service(new Validator(),
   2:             new Repository(new Configuration(),
   3:                            new Logger(new AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency())));

This is manual dependency injection which means exactly what it sounds like. We are manually injecting our dependencies into our service. If you’re interested in reading more about what the pros/cons of doing this instead of using an IoC-container are I think this blog post give a good summary.

 

IoC-container(s)

There are lots of IoC-containers out there. At the top of my head I can think of are StructureMap, Unity, Ninject and Castle Windsor. In all honesty I haven’t done a thorough comparison between the different containers. I simply started with StructureMap and I haven’t had a reason to change (yet). The why I’ve understood it all the frameworks are more similar than different feature wise but differ in syntax etc.

 

StructureMap and syntax

Speaking of the syntax for StructureMap two things can be good to know: The official documentation (linked above) is very extensive but is written in an older version which means that if you’re using a later version (2.5 and up I think, I’m using 2.6.1 in this post) you might find a lot of missing or obsolete methods. The other thing is that the framework makes heavy use of various lambda expressions so if you’re not too familiar with those it might look weird for a second before you get used to it.

 

Basic configuration and auto wiring

Syntax aside, what we’re basically doing is that we give StructureMap some abstraction and then give it instructions to be able to resolve what actual concrete implementation we want to use. So, using the same service example as above when we talk about the abstraction IConfiguration we actually want to use the concrete class Configuration.

After adding a reference to the StructureMap dll configuring this would in it’s simplest form look like this:

   1: ObjectFactory.Configure(x =>
   2: {
   3:     x.For<IConfiguration>()
   4:         .Use<Configuration>();
   5: });

 

And when we want our concrete implementation we simply ask the container for it

   1: var configuration = ObjectFactory.GetInstance<IConfiguration>();

 

If we’ve failed to configure how the container should go from the abstraction to concrete type SM throws a very friendly exception

sm_exception

 

For debugging purposes it can also be very nice to see exactly what is in the container. This can be viewed by calling the (fantastically named) method WhatDoIHave like this

   1: string whatDoIHave = ObjectFactory.WhatDoIHave();

 

It’s tempting to think that to get our service (after configuring the dependencies we have) we would write code like this:

   1: var service = new Service(ObjectFactory.GetInstance<IValidator>(), // rest of dependencies here

This is not a recommended way to work with your container. What we’re doing here is partly more or less the exact code I showed in the beginning and we’ve just tied our service to our container. This is bad since we started with DI to get away from our code having dependencies to other concrete implementations. What we want is to use the concept of auro wiring and let SM figure our ALL the dependencies by itself.

Imagine that when we ask SM for our service it finds it’s greediest (as in has most parameters) constructor. In this constructor it find that it needs a IValidator and a IRepository. It looks in it’s configuration and see that we’ve told it to use the concrete class Validator and Repository. When it tries to create the Repository it sees that it itself needs an IConfiguration and an ILogger. It looks in it’s configuration and so on and so forth until it has resolved all the dependencies. So, to get an instance of our service we simply do this

   1: var service = ObjectFactory.GetInstance<Service>();

 

Auto wiring and (web) frameworks

Unfortunetly for us as EPiServer developers WebForms doesn’t have very good support for doing IoC. Using MVC (which as you saw if you attended Christian Libardos presentation at the Meetup is something that’s in the works) will make using IoC a ton easier.

The reason for this is that we can control the creation of the Controller (which is the creamy stuff in the Oreo cookie that is MVC). Since the logic flows from the controller this makes for an excellent root to call your GetInstance and leverage auto wiring to that you don’t have to reference SM all throughout your code. Even though you can’t control the creation of Page in a WebForms application you can get around it using setter injections (see my post about that here) but it won’t be as pretty.

 

Assembly scanning and conventions

If we look at the configuration for our service

   1: ObjectFactory.Configure(x =>
   2: {
   3:     x.For<IValidator>()
   4:         .Use<Validator>();
   5:  
   6:     x.For<IAmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>()
   7:         .Use<AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>();
   8:  
   9:     x.For<IRepository>()
  10:         .Use<Repository>();
  11:  
  12:     x.For<IConfiguration>()
  13:         .Use<Configuration>();
  14:  
  15:     x.For<ILogger>()
  16:         .Use<Logger>();
  17: });

we can see a pattern emerging. We have some form of abstraction that’s called ISomething and then a concrete implementation called Something. This is quite common and SM has formed a default convention around this. So, to make it easier and skip a lot of boring typing we can ask SM to scan assemblies and add all abstractions/implementations that follows the default convention.

   1: ObjectFactory.Configure(x =>
   2: {
   3:     x.Scan(a =>
   4:                {
   5:                    a.AssembliesFromApplicationBaseDirectory();
   6:                    a.WithDefaultConventions();
   7:                });
   8: });

We can of course control which assemblies we want to scan. In the above example we scan all assemblies from the application base directory (which in an asp.net application would be the bin folder). We can also specify just a single assembly, or the calling assembly or filter assemblies which starts with a certain string or… yea, the sky is the limit!

You can also create your own conventions if you have some sort of coding guidelines on how to do you naming.

 

Registry DSL and bootstrappers

The examples we’ve seen so far have been using the SM Registry DSL, which is the recommended approach to configuration. Usually this is done via a bootstrapper and usage of the Registry class.

Near your applications entry point (for instance, Application_Start in an asp.net application) you call your bootstrapper which in it’s turn looks for all the registries and adds whatever is configured in them.

Creating a registry is as simple as inheriting from the class Registry. In the constructor of your class you have access to the Registry API we’ve seen examples of above. It can be beneficial to group related dependencies in the same registry and name that registry accordingly. Let’s say we want to create a Registry that handles our logging

   1: public class LogRegistry : Registry
   2: {
   3:     public LogRegistry()
   4:     {
   5:         For<ILogger>()
   6:             .Use<Logger>();
   7:  
   8:         For<IAmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>().Use
   9:             <AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>();
  10:     }
  11: }

 

Our bootstrapper looks like this

   1: public class StructureMapBootrstrapper
   2: {
   3:     public static void Bootstrap(IContainer container)
   4:     {
   5:         container.Configure(x =>
   6:             {
   7:                 x.Scan(a =>
   8:                    {
   9:                        a.AssembliesFromApplicationBaseDirectory();
  10:                        a.LookForRegistries();
  11:                    });
  12:             });
  13:     }
  14: }

Note that we pass it an instance of IContainer. We could use ObjectFactory (which simply is a static facade for the main container) directly but we want to keep dependencies to our concrete classes to a minimum.

In the entry point of our application we simply call our Bootstrapper and pass it our container.

   1: Configuration.StructureMapBootrstrapper.Bootstrap(ObjectFactory.Container);

 

In this example we probably wouldn’t have needed a LogRegistry since those registrations (as well as the others we’ve seen before) all could have been registered using the default conventions. Usually what you want is to use the default conventions as much as possible and then do manual registration (using the Registry class) for cases were the convention doesn’t cut it.

 

Switching implementations

For me, one of the great things about using an IoC-container is that it’s very easy to change the way your application behaves without having to change the code directly. It’s simply a matter of configuration. Let’s look at the following example: Our application sends mails via SMTP with the following setup.

   1: public interface ISendMailMessage
   2: {
   3:     void SendMessag(MailMessage message);
   4: }
   1: public class SendMailMessageViaSmtp : ISendMailMessage
   2: {
   3:     public void SendMessag(MailMessage message)
   4:     {
   5:         using (var client = new SmtpClient("mail.something.com"))
   6:         {
   7:             client.Send(message);
   8:         }
   9:     }
  10: }
   1: public class MailRegistry : Registry
   2: {
   3:     public MailRegistry()
   4:     {
   5:         For<IMailService>()
   6:             .Use<MailService>();
   7:  
   8:         For<ISendMailMessage>()
   9:             .Use<SendMailMessageViaSmtp>();
  10:     }
  11: }

When we’re doing our development locally we might not have access to that server or for some other reason don’t want to send mail. We could of course do various “hacks” such as comment/uncomment code in the SendMailMessageViaSmtp class but this rarely leads to a solid solution in the end.

 

Using profiles

There are various better approaches, one of which is using profiles in SM. What this enables is to do is create different configurations for different profiles. For our development machine we’ve created another implementation that logs the mail to a text file

   1: public class LogMailMessage : ISendMailMessage
   2: {
   3:     public void SendMessag(MailMessage message)
   4:     {
   5:         using(var writer = new StreamWriter(@"MailLog.txt", true))
   6:         {
   7:             writer.WriteLine(
   8:                 "Sending mailmessage from {0} to {1} with subject {2}",
   9:                 message.From.Address,
  10:                 message.To,
  11:                 message.Subject
  12:                 );
  13:         }
  14:     }
  15: }

 

In our mailregistry we create a profile call DEBUG and instruct SM to use this class with this profile

   1: For<ISendMailMessage>()
   2:     .Use<SendMailMessageViaSmtp>();
   3:  
   4: Profile("DEBUG", profile =>
   5:             {
   6:                 profile.For<ISendMailMessage>()
   7:                     .Use<LogMailMessage>();
   8:             });

 

And then use whatever logic we see fit to switch to this profile when appropriate

   1: #if DEBUG
   2: ObjectFactory.Profile = "DEBUG";
   3: #endif

 

Using conditional logic

Another way to accomplish switching which implementation to use is by using conditions in our configuration. So, instead of looking at whether we build our application in debug mode or not let’s say that we want to switch our mail implementation depending on our machine name. Using conditionals we can accomplish this with the following configuration.

   1: ObjectFactory.Configure(x =>
   2:     {
   3:         x.For<ISendMailMessage>()
   4:             .ConditionallyUse(c =>
   5:               {
   6:                   c.If(context => Environment.MachineName.Equals("CNNOTE41"))
   7:                       .ThenIt
   8:                       .Is
   9:                       .ConstructedBy(by => new SendMailMessageViaSmtp());
  10:     
  11:                   c.TheDefault
  12:                       .Is
  13:                       .ConstructedBy(by => new LogMailMessage());
  14:               });
  15:     });

 

Primitive constructor arguments

In some constructors you may have a dependency on some primitive types (string, int etc). A classic example of this is some form of repository class which takes a connection string as a constructor parameter. If we for a moment ignore why this may be a bad idea from an architectural point of view how do you do this using SM?

As it turns out it’s as simple as telling SM what we want to use for the parameter in question. Given this repository class

   1: public class Repository : IRepository
   2: {
   3:     private string connectionString;
   4:  
   5:     public Repository(string connectionString)
   6:     {
   7:         this.connectionString = connectionString;
   8:     }
   9:  
  10:     public string GetConnectionString()
  11:     {
  12:         return connectionString;
  13:     }
  14: }

we can inject our connection string of choice using the Ctor method

   1: ObjectFactory.Configure(x =>
   2: {
   3:     x.For<IRepository>()
   4:         .Use<Repository>()
   5:         .Ctor<string>()
   6:         .Is("Stefans connectionstring");
   7: });

 

Since our class only had one constructor parameter of type string SM can figure out the matching. If we’ve had two we would have to specify which of the parameters belonged with which value. To do this you simply specify a name in the Ctor method. The following code will yield the same result as above but explicitly specifies which constructor parameter to set

   1: x.For<IRepository>()
   2:     .Use<Repository>()
   3:     .Ctor<string>("connectionString")
   4:     .Is("Stefans connectionstring");

 

Lists and AddAllTypesOf

Imagine we’re building some sort of pluggable part of our framework where 3rd party assemblies want us to execute their code of a certain interface. The somewhat contrived example for this is a HelloService that takes a list of ISayHello and simply loops through them and executes the hello method on each of them.

   1: public class SayHelloService
   2: {
   3:     private List<ISayHello> helloList;
   4:  
   5:     public SayHelloService(List<ISayHello> helloList)
   6:     {
   7:         this.helloList = helloList;
   8:     }
   9:  
  10:     public void SayHello()
  11:     {
  12:         helloList.ForEach(x => Console.Out.WriteLine(x.Hello()));
  13:     }
  14: }
   1: public interface ISayHello
   2: {
   3:     string Hello();
   4: }

 

We can instruct SM to scan assemblies according to some logic and then add all types it can find of a certain interface.

   1: public class SayHelloRegistry : Registry
   2: {
   3:     public SayHelloRegistry()
   4:     {
   5:         Scan(a =>
   6:         {
   7:             a.AssembliesFromApplicationBaseDirectory();
   8:             a.AddAllTypesOf<ISayHello>();
   9:         });
  10:     }
  11: }

 

So if we have two concrete implementations of the ISayHello interface

   1: public class SayHelloInEnglish : ISayHello
   2: {
   3:     public string Hello()
   4:     {
   5:         return "Hello";
   6:     }
   7: }
   8:  
   9: public class SayHelloInSwedish : ISayHello
  10: {
  11:     public string Hello()
  12:     {
  13:         return "Tjena";
  14:     }
  15: }

SM is smart enough to figure out that since we have a dependency to a list of ISayHello it sends all the implementations it can find in it’s configuration. So, as long as the 3rd party that wants to use your plugin system implements an agreed upon interface and you in turn scans their assembly it will be sent to you function automatically.

 

Is that all?

There are of course more to SM than I’ve written about here but these are the parts of it that I’ve used most in my projects so far.

 

Example solution

I’ve made a VS-2010 solution that contains examples of the various SM features listed above. You can download it here (at your own risk bla bla bla).

Very nice Stefan! Thanks for this.

Great post, thanks :)

Thanks for the post :)
I have a question regarding your comment on WebForms.
"Unfortunetly for us as EPiServer developers WebForms doesn’t have very good support for doing IoC"

Do you have any recommendations what to do in WebForms?
I'm thinking of using the ObjectFactory.GetInstance in WebForms instead of using dependency Injection. And then use dependency injection elsewhere.

could fix the download link please.

Please login to comment.