Skip to content
On this page

Try Getting an Optional Service by Service Type

INFO

The Lamar team does not recommend using "optional" dependencies as shown in this topic, but external frameworks like ASP.Net MVC and Web API use this concept in their IoC container integration, so here it is. The Lamar team prefers the usage of the Nullo pattern instead.

In normal usage, if you ask Lamar for a service and Lamar doesn't recognize the requested type, the requested name, or know what the default should be for that type, Lamar will fail fast by throwing an exception rather than returning a null. Sometimes though, you may want to retrieve an optional service from Lamar that may or may not be registered in the Container. If that particular registration doesn't exist, you just want a null value. Lamar provides first class support for optional dependencies through the usage of the IContainer.TryGetInstance() methods.

INFO

In Lamar, the ASP.Net Core IServiceProvider.GetService() method has the same functionality and meaning as the TryGetInstance() method. If you were wondering how Lamar's StructureMap-flavored GetInstance() method is different, that's how.

Say you have a simple interface IFoo that may or may not be registered in the Container:

cs
public interface IFoo
{
}

public class Foo : IFoo
{
}

snippet source | anchor

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 Lamar doesn't have a registration for IFoo:

cs
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();

}

snippet source | anchor

Just to make this perfectly clear, if Lamar has a default registration for IFoo, you get this behavior:

cs
[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();
}

snippet source | anchor

If Lamar knows nothing about IFoo, you get a null:

cs
[Fact]
public void i_do_not_have_that()
{
    var container = new Container();

    container.TryGetInstance<IFoo>()
        .ShouldBeNull();

    // -- or --

    container.TryGetInstance(typeof(IFoo))
        .ShouldBeNull();
}

snippet source | anchor

Concrete Types

Since it's not a perfect world, there are some gotchas you need to be aware of. While Lamar will happily auto-resolve concrete types that aren't registered, that does not apply to the TryGetInstance mechanism:

cs
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();
}

snippet source | anchor

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:

cs
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>>();
}

snippet source | anchor