New for StructureMap 3.0 is a feature to create missing service registrations at runtime based on pluggable rules using the new IFamilyPolicy
interface:
public interface IFamilyPolicy
{
PluginFamily Build(Type type);
bool AppliesToHasFamilyChecks { get; }
}
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 PluginFamily
object for that plugin type that models the registrations for that plugin type, including the default, additional named instances, interceptors or decorators, and lifecycle rules.
The simplest built in example is the EnumerableFamilyPolicy
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
:
public class EnumerableFamilyPolicy : IFamilyPolicy
{
public PluginFamily Build(Type type)
{
if (EnumerableInstance.IsEnumerable(type))
{
var family = new PluginFamily(type);
family.SetDefault(new AllPossibleInstance(type));
return family;
}
return null;
}
public bool AppliesToHasFamilyChecks
{
get
{
return false;
}
}
}
The result of EnumerableFamilyPolicy
in action is shown by the acceptance test below:
[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));
}
See also the Handling Missing Named Instances for runtime determination of named instances within a known plugin type.
Built In Policies
StructureMap and StructureMap.AutoMocking use several IFamilyPolicy
rules internally to create default behavior. In all cases, any custom
IFamilyPolicy
rule that you explicitly add to a Container
will be evaluated before the built in policies.
- CloseGenericFamilyPolicy - uses the registration for an open type as a template to create the registrations for a closed type the first time StructureMap encounters that closed type. See Generic Types for more information.
- FuncBuildByNamePolicy -
Used internally to create registrations for
Func<string, T>
builders. - EnumerableFamilyPolicy - Shown above.
- AutoMockedContainer - Used by StructureMap.AutoMocking to create registrations on the fly that just return a mock object for the requested plugin type.
Using a Custom IFamilyPolicy" id="custom">
FubuMVC 2.0 (still unreleased to the public as of yet, but in production usage) uses a custom family policy in its StructureMap integration to auto-resolve concrete configuration types like the following type:
public class SomeSettings
{
public string ThisDirectory { get; set; }
public string ThatDirectory { get; set; }
}
Unless the system using this object has explicitly registered SomeSettings
, we want StructureMap to resolve this object by
using data from the basic .Net appSettings collection to create a SomeSettings
object.
For the sake of the example, assume that you have a functioning service that implements this interface below:
public interface ISettingsProvider
{
T SettingsFor<T>() where T : class, new();
object SettingsFor(Type settingsType);
}
public class AppSettingsProvider : ISettingsProvider
{
public T SettingsFor<T>() where T : class, new()
{
return SettingsFor(typeof (T)).As<T>();
}
public object SettingsFor(Type settingsType)
{
// The real one reads key/value data from
// the appSettings and uses FubuCore's
// model binding to assign data to a new
// object of settingsType
return null;
}
}
Assuming that ISettingsProvider
is registered in your StructureMap Container
, you could then craft a custom
IFamilyPolicy
class like this:
public class SettingPolicy : IFamilyPolicy
{
public PluginFamily Build(Type type)
{
if (type.Name.EndsWith("Settings") && type.IsConcreteWithDefaultCtor())
{
var family = new PluginFamily(type);
var instance = buildInstanceForType(type);
family.SetDefault(instance);
return family;
}
return null;
}
public bool AppliesToHasFamilyChecks
{
get { return true; }
}
private static Instance buildInstanceForType(Type type)
{
var instanceType = typeof (SettingsInstance<>).MakeGenericType(type);
var instance = Activator.CreateInstance(instanceType).As<Instance>();
return instance;
}
}
// SettingsInstance just uses the registered service for ISettingsProvider to
// build the real object
public class SettingsInstance<T> : LambdaInstance<T> where T : class, new()
{
public SettingsInstance() : base("Building {0} from application settings".ToFormat(typeof (T).FullName),
c => c.GetInstance<ISettingsProvider>().SettingsFor<T>())
{
}
}
SettingPolicy
is able to create a registration on the fly for any concrete type whose name ends in "Settings" and has a default, no arg
constructor.
To use register the custom SettingPolicy
, use one of the Registry.Policies.OnMissingFamily()
methods:
public class SettingsRegistry : Registry
{
public SettingsRegistry()
{
For<ISettingsProvider>().Use<AppSettingsProvider>();
Policies.OnMissingFamily<SettingPolicy>();
}
}
You can see the real implementation of the SettingPolicy
in action in its integration tests on GitHub.