Appearance
Policies
Lamar offers some mechanisms to conventionally determine missing registrations at runtime or apply some fine-grained control over how services are constructed. All of these mechanisms are available under the ServiceRegistry.Policies
property when configuring a Lamar container.
For more information on type scanning conventions, see auto-registration and conventions
Auto Resolve Missing Services
INFO
These policies are only evaluated the first time that a particular service type is requested through the container.
Lamar has a feature to create missing service registrations at runtime based on pluggable rules using the new IFamilyPolicy
interface:
cs
public interface IFamilyPolicy : ILamarPolicy
{
/// <summary>
/// Allows you to create missing registrations for an unknown service type
/// at runtime.
/// Return null if this policy does not apply to the given type
/// </summary>
ServiceFamily Build(Type type, ServiceGraph serviceGraph);
}
Internally, if you make a request to IContainer.GetInstance(type)
for a type that the active Container
does not recognize, StructureMap will next try to apply all the registered IFamilyPolicy
policies to create a ServiceFamily
object for that service type that models the registrations for that service type, including the default, additional named instances, interceptors or decorators, and lifecycle rules.
The simplest built in example is the EnumerablePolicy
shown below that can fill in requests for IList<T>
, ICollection<T>
, and T[]
with a collection of all the known registrations of the type T
:
cs
internal class EnumerablePolicy : IFamilyPolicy
{
public ServiceFamily Build(Type type, ServiceGraph serviceGraph)
{
if (type.IsArray)
{
var instanceType = typeof(ArrayInstance<>).MakeGenericType(type.GetElementType());
var instance = Activator.CreateInstance(instanceType, type).As<Instance>();
return new ServiceFamily(type, new IDecoratorPolicy[0], instance);
}
if (type.IsEnumerable())
{
var elementType = type.GetGenericArguments().First();
var instanceType = typeof(ListInstance<>).MakeGenericType(elementType);
var ctor = instanceType.GetConstructors().Single();
var instance = ctor.Invoke(new object[] { type }).As<Instance>();
return new ServiceFamily(type, new IDecoratorPolicy[0], instance);
}
return null;
}
}
The result of EnumerablePolicy
in action is shown by the acceptance test below:
cs
[Fact]
public void collection_types_are_all_possible_by_default()
{
// NOTE that we do NOT make any explicit registration of
// IList<IWidget>, IEnumerable<IWidget>, ICollection<IWidget>, or IWidget[]
var container = new Container(_ =>
{
_.For<IWidget>().Add<AWidget>();
_.For<IWidget>().Add<BWidget>();
_.For<IWidget>().Add<CWidget>();
});
// IList<T>
container.GetInstance<IList<IWidget>>()
.Select(x => x.GetType())
.ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));
// ICollection<T>
container.GetInstance<ICollection<IWidget>>()
.Select(x => x.GetType())
.ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));
// Array of T
container.GetInstance<IWidget[]>()
.Select(x => x.GetType())
.ShouldHaveTheSameElementsAs(typeof(AWidget), typeof(BWidget), typeof(CWidget));
}
For another example, consider this example from the Lamar unit tests. Say you have a service that looks like this:
cs
public class Color
{
public string Name { get; set; }
}
And you build a policy that auto-resolves registrations for the Color
service if none previously exist:
cs
public class ColorPolicy : IFamilyPolicy
{
public ServiceFamily Build(Type type, ServiceGraph serviceGraph)
{
if (type != typeof(Color))
{
return null;
}
return new ServiceFamily(type, serviceGraph.DecoratorPolicies,
ObjectInstance.For(new Color { Name = "Red" }).Named("Red"),
ObjectInstance.For(new Color { Name = "Blue" }).Named("Blue"),
ObjectInstance.For(new Color { Name = "Green" }).Named("Green")
);
}
}
You can register the new ColorPolicy
shown above like this:
cs
var container = Container.For(_ => { _.Policies.OnMissingFamily<ColorPolicy>(); });
Internally, Lamar uses this IFamilyPolicy
feature for its generic type support, the enumerable type support described as above, and the auto registration of concrete types.
Instance Construction Policies
Lamar allows you to create conventional build policies with a mechanism for altering how object instances are built based on user created meta-conventions using the IInstancePolicy
shown below:
cs
/// <summary>
/// Custom policy on Instance construction that is evaluated
/// as part of creating a "build plan"
/// </summary>
public interface IInstancePolicy : ILamarPolicy
{
/// <summary>
/// Apply any conventional changes to the configuration
/// of a single Instance
/// </summary>
/// <param name="instance"></param>
void Apply(Instance instance);
}
These policies are registered as part of the ServiceRegistry DSL with the Policies.Add()
method:
cs
var container = new Container(_ =>
{
_.Policies.Add<MyCustomPolicy>();
// or
_.Policies.Add(new MyCustomPolicy());
});
cs
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:
cs
/// <summary>
/// Base class for using policies against IConfiguredInstance registrations
/// </summary>
public abstract class ConfiguredInstancePolicy : IInstancePolicy
{
public void Apply(Instance instance)
{
if (instance is IConfiguredInstance)
{
apply(instance.As<IConfiguredInstance>());
}
}
protected abstract void apply(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:
cs
public class DatabaseUser
{
public DatabaseUser(string connectionString)
{
ConnectionString = connectionString;
}
public string ConnectionString { get; set; }
}
public class ConnectedThing
{
public ConnectedThing(string connectionString)
{
ConnectionString = connectionString;
}
public string ConnectionString { get; set; }
}
cs
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:
cs
public class ConnectionStringPolicy : ConfiguredInstancePolicy
{
protected override void apply(IConfiguredInstance instance)
{
var parameter = instance.ImplementationType
.GetConstructors()
.SelectMany(x => x.GetParameters())
.FirstOrDefault(x => x.Name == "connectionString");
if (parameter != null)
{
var connectionString = findConnectionStringFromConfiguration();
instance.Ctor<string>(parameter.Name).Is(connectionString);
}
}
// find the connection string from whatever configuration
// strategy your application uses
private string findConnectionStringFromConfiguration()
{
return "the connection string";
}
}
cs
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:
cs
[Fact]
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");
}
cs
[Fact]
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 named Database/IDatabase
like so:
cs
public interface IDatabase
{
}
public class Database : IDatabase
{
public Database(string connectionString)
{
ConnectionString = connectionString;
}
public string ConnectionString { get; set; }
public override string ToString()
{
return string.Format("ConnectionString: {0}", ConnectionString);
}
}
cs
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:
cs
public class InjectDatabaseByName : ConfiguredInstancePolicy
{
protected override void apply(IConfiguredInstance instance)
{
var parameterInfos = instance.ImplementationType.GetConstructors()
.SelectMany(x => x.GetParameters())
.Where(x => x.ParameterType == typeof(IDatabase));
foreach (var param in parameterInfos)
// Using ReferencedInstance here tells Lamar
// to "use the IDatabase by this name"
instance.Ctor<IDatabase>(param.Name).IsNamedInstance(param.Name);
}
}
cs
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:
cs
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>();
});
cs
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:
cs
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; }
public IDatabase Red { get; }
}
cs
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:
cs
// 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*");
cs
// 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:
cs
public class CacheIsSingleton : IInstancePolicy
{
public void Apply(Instance instance)
{
if (instance.ImplementationType.Name.EndsWith("Cache"))
{
instance.Lifetime = ServiceLifetime.Singleton;
}
}
}
cs
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:
cs
[Fact]
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>()
.ShouldBeSameAs(container.GetInstance<IWidgets>());
// Now that the policy has executed, we
// can verify that WidgetCache is a SingletonThing
container.Model.For<IWidgets>().Default
.Lifetime.ShouldBe(ServiceLifetime.Singleton);
}
cs
[Fact]
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 SingletonThing
container.Model.For<IWidgets>().Default
.Lifecycle.ShouldBeOfType<SingletonLifecycle>();
}