In normal usage, if you ask StructureMap for a service and StructureMap doesn't recognize the requested type, the requested name, or know what the default should be for that type, StructureMap will fail fast by throwing an exception rather than returning a null. Sometimes though, you may want to
retrieve an optional service from StructureMap that may or may not be registered in the Container. If that particular registration doesn't exist, you
just want a null value. StructureMap provides first class support for optional dependencies through the usage of the IContainer.TryGetInstance()
methods.
Say you have a simple interface IFoo
that may or may not be registered in the Container:
public interface IFoo
{
}
public class Foo : IFoo
{
}
In your own code you might request the IFoo
service like the code below, knowing that you'll
take responsibility yourself for building the IFoo
service if StructureMap doesn't have a registration
for IFoo
:
public class MyFoo : IFoo
{
}
[Fact]
public void real_usage()
{
var container = new Container();
// if the container doesn't know about it,
// I'll build it myself
var foo = container.TryGetInstance<IFoo>()
?? new MyFoo();
}
Just to make this perfectly clear, if StructureMap has a default registration for IFoo
, you get this behavior:
[Fact]
public void i_have_got_that()
{
var container = new Container(_ => _.For<IFoo>().Use<Foo>());
container.TryGetInstance<IFoo>()
.ShouldNotBeNull();
// -- or --
container.TryGetInstance(typeof(IFoo))
.ShouldNotBeNull();
}
If StructureMap knows nothing about IFoo
, you get a null:
[Fact]
public void i_do_not_have_that()
{
var container = new Container();
container.TryGetInstance<IFoo>()
.ShouldBeNull();
// -- or --
container.TryGetInstance(typeof(IFoo))
.ShouldBeNull();
}
Concrete Types
Since it's not a perfect world, there are some gotchas you need to be aware of.
While StructureMap will happily auto-resolve concrete types that aren't registered,
that does not apply to the TryGetInstance
mechanism:
public class ConcreteThing
{
}
[Fact]
public void no_auto_resolution_of_concrete_types()
{
var container = new Container();
container.TryGetInstance<ConcreteThing>()
.ShouldBeNull();
// now register ConcreteThing and do it again
container.Configure(_ => { _.For<ConcreteThing>().Use<ConcreteThing>(); });
container.TryGetInstance<ConcreteThing>()
.ShouldNotBeNull();
}
Optional Generic Types
If you are using open generic types, the TryGetInstance()
mechanism can close the open generic registration
to satisfy the optional dependency like this sample:
public interface IThing<T>
{
}
public class Thing<T> : IThing<T>
{
}
[Fact]
public void can_try_get_open_type_resolution()
{
var container = new Container(_ => { _.For(typeof(IThing<>)).Use(typeof(Thing<>)); });
container.TryGetInstance<IThing<string>>()
.ShouldBeOfType<Thing<string>>();
}