New Construction Policies in StructureMap 4.0

This is taken from some brand spanking new StructureMap docs on construction policies. As part of the forthcoming StructureMap 4.0 release this month I’m hoping to flood my blog with little posts on StructureMap. The code samples look a lot better in the actual docs, mea culpa.

StructureMap has long supported conventional policies for registration based on type scanning and 3.0 introduced cleaner mechanisms for interception policies. The 4.0 release extends StructureMap’s support for conventional build policies with a new mechanism for altering how object instances are built based on user created meta-conventions using the IInstancePolicy shown below:

    /// <summary>
    /// Custom policy on Instance construction that is evaluated
    /// as part of creating a "build plan"
    /// </summary>
    public interface IInstancePolicy
    {
        /// <summary>
        /// Apply any conventional changes to the configuration
        /// of a single Instance
        /// </summary>
        /// <param name="pluginType"></param>
        /// <param name="instance"></param>
        void Apply(Type pluginType, Instance instance);
    }

These policies are registered as part of the registry dsl with the Policies.Add() method:

            var container = new Container(_ =>
            {
                _.Policies.Add<MyCustomPolicy>();
                // or
                _.Policies.Add(new MyCustomPolicy());
            });

The IInstancePolicy mechanism probably works differently than other IoC containers in that the policy is applied to the container’s underlying configuration model instead of at runtime. Internally, StructureMap lazily creates a “build plan” for each configured Instance at the first time that that Instance is built or resolved. As part of creating that build plan, StructureMap runs all the registered IInstancePolicy objects against the Instance in question to capture any potential changes before “baking” the build plan into a .Net Expression that is then compiled into a Func for actual construction.

The Instance objects will give you access to the types being created, the configured name of the Instance (if any), the ability to add interceptors and to modify the lifecycle. If you wish to add inline dependencies to Instances that are built by calling constructor function and setter properties, you may find it easier to use the ConfiguredInstancePolicy base class as a convenience:

    public abstract class ConfiguredInstancePolicy : IInstancePolicy
    {
        public void Apply(Type pluginType, Instance instance)
        {
            var configured = instance as IConfiguredInstance;
            if (configured != null)
            {
                apply(pluginType, configured);
            }
        }

        // This method is called against any Instance that implements 
        // the IConfiguredInstance interface
        protected abstract void apply(Type pluginType, IConfiguredInstance instance);
    }

For more information, see:

Example 1: Constructor arguments

So let me say upfront that I don’t like this approach, but other folks have asked for this ability over the years. Say that you have some legacy code where many concrete classes have a constructor argument called “connectionString” that needs to be the connection string to the application database like these classes:

        public class DatabaseUser
        {
            public string ConnectionString { get; set; }

            public DatabaseUser(string connectionString)
            {
                ConnectionString = connectionString;
            }
        }

        public class ConnectedThing
        {
            public string ConnectionString { get; set; }

            public ConnectedThing(string connectionString)
            {
                ConnectionString = connectionString;
            }
        }

Instead of explicitly configuring every single concrete class in StructureMap with that inline constructor argument, we can make a policy to do that in one place:

        public class ConnectionStringPolicy : ConfiguredInstancePolicy
        {
            protected override void apply(Type pluginType, IConfiguredInstance instance)
            {
                var parameter = instance.Constructor.GetParameters().FirstOrDefault(x => x.Name == "connectionString");
                if (parameter != null)
                {
                    var connectionString = findConnectionStringFromConfiguration();
                    instance.Dependencies.AddForConstructorParameter(parameter, connectionString);
                }
            }

            // find the connection string from whatever configuration
            // strategy your application uses
            private string findConnectionStringFromConfiguration()
            {
                return "the connection string";
            }
        }

Now, let’s use that policy against the types that need “connectionString” and see what happens:

        [Test]
        public void use_the_connection_string_policy()
        {
            var container = new Container(_ =>
            {
                _.Policies.Add<ConnectionStringPolicy>();
            });

            container.GetInstance<DatabaseUser>()
                .ConnectionString.ShouldBe("the connection string");

            container.GetInstance<ConnectedThing>()
                .ConnectionString.ShouldBe("the connection string");
        }

Years ago StructureMap was knocked by an “IoC expert” for not having this functionality. I said at the time — and still would — that I would strongly recommend that you simply don’t directly open database connections in more than one or a very few spots in your code anyway. If I did need to configure a database connection string in multiple concrete classes, I prefer strong typed configuration.

Example 2: Connecting to Databases based on Parameter Name

From another common user request over the years, let’s say that your application needs to connect to multiple databases, but your data access service in both cases is an interface called IDatabase, and that’s all the consumers of any database should ever need to know.

To make this concrete, let’s say that our data access is all behind an interface and concrete class pair namedDatabase/IDatabase like so:

        public interface IDatabase { }

        public class Database : IDatabase
        {
            public string ConnectionString { get; set; }

            public Database(string connectionString)
            {
                ConnectionString = connectionString;
            }

            public override string ToString()
            {
                return string.Format("ConnectionString: {0}", ConnectionString);
            }
        }

For a registration policy, let’s say that the parameter name of an IDatabase dependency in a constructor function should match an identifier of one of the registered IDatabase services. That policy would be:

        public class InjectDatabaseByName : ConfiguredInstancePolicy
        {
            protected override void apply(Type pluginType, IConfiguredInstance instance)
            {
                instance.Constructor.GetParameters()
                    .Where(x => x.ParameterType == typeof (IDatabase))
                    .Each(param =>
                    {
                        // Using ReferencedInstance here tells StructureMap
                        // to "use the IDatabase by this name"
                        var db = new ReferencedInstance(param.Name);
                        instance.Dependencies.AddForConstructorParameter(param, db);
                    });
            }
        }

And because I’m generally pretty boring about picking test data names, let’s say that two of our databases are named “red” and “green” with this container registration below:

            var container = new Container(_ =>
            {
                _.For<IDatabase>().Add<Database>().Named("red")
                    .Ctor<string>("connectionString").Is("*red*");

                _.For<IDatabase>().Add<Database>().Named("green")
                    .Ctor<string>("connectionString").Is("*green*");
                
                _.Policies.Add<InjectDatabaseByName>();
            });

For more context, the classes that use IDatabase would need to have constructor functions like these below:

        public class BigService
        {
            public BigService(IDatabase green)
            {
                DB = green;
            }

            public IDatabase DB { get; set; }
        }

        public class ImportantService
        {
            public ImportantService(IDatabase red)
            {
                DB = red;
            }

            public IDatabase DB { get; set; }
        }

        public class DoubleDatabaseUser
        {
            public DoubleDatabaseUser(IDatabase red, IDatabase green)
            {
                Red = red;
                Green = green;
            }

            // Watch out for potential conflicts between setters
            // and ctor params. The easiest thing is to just make
            // setters private
            public IDatabase Green { get; private set; }
            public IDatabase Red { get; private set; }
        }

Finally, we can exercise our new policy and see it in action:

            // ImportantService should get the "red" database
            container.GetInstance<ImportantService>()
                .DB.As<Database>().ConnectionString.ShouldBe("*red*");

            // BigService should get the "green" database
            container.GetInstance<BigService>()
                .DB.As<Database>().ConnectionString.ShouldBe("*green*");

            // DoubleDatabaseUser gets both
            var user = container.GetInstance<DoubleDatabaseUser>();

            user.Green.As<Database>().ConnectionString.ShouldBe("*green*");
            user.Red.As<Database>().ConnectionString.ShouldBe("*red*");

How I prefer to do this – my strong preference would be to use separate interfaces for the different databases even if that type is just an empty type marker that implements the same base. I feel like using separate interfaces makes the code easier to trace and understand than trying to make StructureMap vary dependencies based on naming conventions or what namespace a concrete type happens to be in. At least now though, you have the choice of my way or using policies based on naming conventions.

Example 3: Make objects singletons based on type name

Unlike the top two examples, this is taken from a strategy that I used in FubuMVC for its service registration. In that case, we wanted any concrete type whose name ended with “Cache” to be a singleton in the container registration. With the new IInstancePolicy feature in StructureMap 4, we could create a new policy class like so:

        public class CacheIsSingleton : IInstancePolicy
        {
            public void Apply(Type pluginType, Instance instance)
            {
                if (instance.ReturnedType.Name.EndsWith("Cache"))
                {
                    instance.SetLifecycleTo<SingletonLifecycle>();
                }
            }
        }

Now, let’s say that we have an interface named IWidgets and a single implementation called WidgetCache that should track our widgets in the application. Using our new policy, we should see WidgetCache being made a singleton:

        [Test]
        public void set_cache_to_singleton()
        {
            var container = new Container(_ =>
            {
                _.Policies.Add<CacheIsSingleton>();

                _.For<IWidgets>().Use<WidgetCache>();
            });

            // The policy is applied *only* at the time
            // that StructureMap creates a "build plan"
            container.GetInstance<IWidgets>()
                .ShouldBeTheSameAs(container.GetInstance<IWidgets>());

            // Now that the policy has executed, we 
            // can verify that WidgetCache is a singleton
            container.Model.For<IWidgets>().Default
                .Lifecycle.ShouldBeOfType<SingletonLifecycle>();
        }

 

Advertisements

One thought on “New Construction Policies in StructureMap 4.0

  1. Pingback: Dew Drop – October 6, 2015 (#2105) | Morning Dew

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s