Skip to content
On this page

ServiceRegistry DSL

INFO

Lamar's ServiceRegistry is the equivalent to StructureMap's Registry, but the name was changed to disambiguate from the nearly infinite number of other Registry types in .NET.

Creating ServiceRegistry classes is the recommended way of using the Registry DSL.

The Registry DSL is mostly a fluent interface with some nested closure usage. The intent of the Registry DSL is to make the configuration process as error free as possible by using "compiler safe" expressions and defensive programming to point out missing data.

The ServiceRegistry Class

On all but the smallest systems, the main unit of configuration will probably be the ServiceRegistry class. Typically, you would subclass the ServiceRegistry class, then use the fluent interface methods exposed by the Registry class to create Container configuration. Here's a sample ServiceRegistry class below used to configure an instance of an IWidget interface:

cs
public class PurpleRegistry : ServiceRegistry
{
    public PurpleRegistry()
    {
        For<IWidget>().Use<AWidget>();
    }
}

snippet source | anchor

Including Other ServiceRegistry Objects

The next question is "how does my new ServiceRegistry class get used?"

When you set up a Container, you need to simply direct the Container to use the configuration in that ServiceRegistry class:

cs
[Fact]
public void include_a_registry()
{
    var registry = new Registry();
    registry.IncludeRegistry<YellowBlueRegistry>();
    registry.IncludeRegistry<RedGreenRegistry>();
    registry.IncludeRegistry<PurpleRegistry>();
    // build a container
    var container = new Container(registry);
    // verify the default implementation and total registered implementations
    container.GetInstance<IWidget>().ShouldBeOfType<AWidget>();
    container.GetAllInstances<IWidget>().Count().ShouldBe(5);
}

snippet source | anchor

Use versus Add

WARNING

This behavior changed from StructureMap. Lamar follows the now common approach mandated by ASP.Net Core that the last registration for a certain service type wins. So no more special meaning to Use() vs. Add().

There is no difference in behavior between Use and Add in Lamar. The two methods are synonyms and mostly remain in Lamar to provide and easier migration path from StructureMap. The

Registrations with For().Use()/Add()

To register an Instance of a type, the syntax is one of the Registry.For().Use() overloads shown below:

cs
public class SettingDefaults : ServiceRegistry
{
    public SettingDefaults()
    {
        // If you know the plugin type and its a closed type
        // you can use this syntax
        For<IWidget>().Use<DefaultWidget>();

        // By Lambda
        For<IWidget>().Use(() => new DefaultWidget());

        // Pre-existing object
        For<IWidget>().Use(new AWidget());

        // This is rare now, but still valid
        For<IWidget>().Add<AWidget>().Named("A");
        For<IWidget>().Add<BWidget>().Named("B");
        For<IWidget>().Use("A"); // makes AWidget the default

        // Also rare, but you can supply an Instance object
        // yourself for special needs
        For<IWidget>().UseInstance(new MySpecialInstance());

        // If you're registering an open generic type
        // or you just have Type objects, use this syntax
        For(typeof (IService<>)).Use(typeof (Service<>));

        // This is occasionally useful for generic types
        For(typeof (IService<>)).Use(new MySpecialInstance());
    }
}

snippet source | anchor

or

cs
public class AdditionalRegistrations : ServiceRegistry
{
    public AdditionalRegistrations()
    {
        // If you know the plugin type and its a closed type
        // you can use this syntax
        For<IWidget>().Add<DefaultWidget>();

        // By Lambda
        For<IWidget>().Add(() => new DefaultWidget());

        // Pre-existing object
        For<IWidget>().Add(new AWidget());

        // Also rare, but you can supply an Instance object
        // yourself for special needs
        For<IWidget>().AddInstance(new MySpecialInstance());

        // If you're registering an open generic type
        // or you just have Type objects, use this syntax
        For(typeof(IService<>)).Add(typeof(Service<>));

        // This is occasionally useful for generic types
        For(typeof(IService<>)).Add(new MySpecialInstance());
    }
}

snippet source | anchor

Add Many Registrations with For().AddInstances()

If you need to add several Instances to a single service type, the AddInstances() syntax shown below may be quicker and easier to use:

cs
// registry is a StructureMap Registry object
registry.For<IService>().AddInstances(x =>
{
    // Equivalent to For<IService>().Add<ColorService>().....
    x.Type<ColorService>().Named("Red").Ctor<string>("color").Is("Red");

    // Equivalent to For<IService>().Add(new ColorService("Yellow"))......
    x.Object(new ColorService("Yellow")).Named("Yellow");

    // Equivalent to For<IService>().Use(() => new ColorService("Purple"))....
    x.ConstructedBy(() => new ColorService("Purple")).Named("Purple");

    x.Type<ColorService>().Named("Decorated").Ctor<string>("color").Is("Orange");
});

snippet source | anchor

Named Instances

When you have multiple implementations of an interface, it can often be useful to name instances. To retrieve a specific implementation:

cs
[Fact]
public void SimpleCaseWithNamedInstance()
{
    container = new Container(x => { x.For<IWidget>().Add<AWidget>().Named("MyInstance"); });
    // retrieve an instance by name
    var widget = (AWidget)container.GetInstance<IWidget>("MyInstance");
    widget.ShouldNotBeNull();
}

snippet source | anchor

You can also register named instances with the following shorthand:

cs
[Fact]
public void A_concrete_type_is_available_by_name_when_it_is_added_by_the_shorthand_mechanism()
{
    IContainer container = new Container(r => r.For<IAddTypes>().AddInstances(x =>
    {
        x.Type<RedAddTypes>().Named("Red");
        x.Type<GreenAddTypes>().Named("Green");
        x.Type<BlueAddTypes>().Named("Blue");
        x.Type<PurpleAddTypes>();
    }));
    // retrieve the instances by name
    container.GetInstance<IAddTypes>("Red").IsType<RedAddTypes>();
    container.GetInstance<IAddTypes>("Green").IsType<GreenAddTypes>();
    container.GetInstance<IAddTypes>("Blue").IsType<BlueAddTypes>();
}

snippet source | anchor

Inverse Registrations with Use().For()

In some scenarios, a type may implement multiple interfaces. You could register this with a separate For().Use() line for each interface, but if the type is to be a singleton, then registering it this way will give you a different singleton instance for each interface. To use the same instance across multiple interfaces, you can use the reverse syntax.

cs
[Fact]
public void when_singleton_both_interfaces_give_same_instance()
{
    var container = new Container(services =>
    {
        services.Use<Implementation>()
            .Singleton()
            .For<IServiceA>()
            .For<IServiceB>();
    });

    var instanceA = container.GetInstance<IServiceA>();
    var instanceB = container.GetInstance<IServiceB>();

    instanceA.ShouldBeTheSameAs(instanceB);
}

snippet source | anchor

The same thing works for scoped registrations; using .Scoped() in place of .Singleton() in the above sample would result in the same instance being returned when resolving any one of the registered interfaces for the duration of the scope.

A transient registration can also be made using .Transient(), in which case the behaviour is exactly the same as with the more usual For().Use() syntax; it's just a convenient shorthand in the case of a type that implements many interfaces.