Fork me on GitHub

Auto-Registration and Conventions Edit on GitHub


StructureMap has rich support for registering types by scanning assemblies and applying conventional registrations. Between scanning and default conventions, configurations are often just a few lines.

Also see Type Scanning Diagnostics for help in understanding the assembly scanning behavior in your system.

Registry.Scan()

Assembly scanning operations are defined by the Registry.Scan() method demonstrated below:


public class BasicScanning : Registry
{
    public BasicScanning()
    {
        Scan(_ =>
        {
            // Declare which assemblies to scan
            _.Assembly("StructureMap.Testing");
            _.AssemblyContainingType<IWidget>();

            // Filter types
            _.Exclude(type => type.Name.Contains("Bad"));

            // A custom registration convention
            _.Convention<MySpecialRegistrationConvention>();

            // Built in registration conventions
            _.AddAllTypesOf<IWidget>().NameBy(x => x.Name.Replace("Widget", ""));
            _.WithDefaultConventions();
        });
    }
}

Please note (because I've been asked this several times over the years) that each call to Registry.Scan() is an entirely atomic operation that has no impact on previous or subsequent calls.

Any given call to Registry.Scan() consists of three different things:

  1. One or more assemblies to scan for types
  2. One or more registration conventions
  3. Optionally, set filters to only include certain types or exclude other types from being processed by the scanning operation

Scan the Calling Assembly

One of the easiest ways to register types is by scanning the assembly your registry is placed in.

Note if you have other registries, StructureMap will not automatically find them.


[Fact]
public void scan_but_ignore_registries_by_default()
{
    Scan(x => { x.TheCallingAssembly(); });

    TestingRegistry.WasUsed.ShouldBeFalse();
}


Note that this method is an extension method in the StructureMap.Net4 assembly and cannot be used if you target PCL compliance.

Scan for Registries

StructureMap can automatically include other registries with theLookForRegistries method.


[Fact]
public void Search_for_registries_when_explicitly_told()
{
    Scan(x =>
    {
        x.TheCallingAssembly();
        x.LookForRegistries();
    });

    TestingRegistry.WasUsed.ShouldBeTrue();
}


As of 4.0, this operation is now recursive and StructureMap has always been idempotent about adding Registry types

Search for Assemblies on the File System

StructureMap provides facilities for registering types by finding assemblies in the application bin path:


[Fact]
public void scan_all_assemblies_in_a_folder()
{
    Scan(x => x.AssembliesFromPath(assemblyScanningFolder));
    shouldHaveFamilyWithSameName<IInterfaceInWidget5>();
    shouldHaveFamilyWithSameName<IWorker>();
    shouldNotHaveFamilyWithSameName<IDefinedInExe>();
}

[Fact]
public void scan_all_assemblies_in_application_base_directory()
{
    Scan(x => x.AssembliesFromApplicationBaseDirectory());
    shouldHaveFamilyWithSameName<IInterfaceInWidget5>();
    shouldHaveFamilyWithSameName<IWorker>();
    shouldNotHaveFamilyWithSameName<IDefinedInExe>();
}


Do note that StructureMap 4.0 does not search for .exe files in the assembly search. The StructureMap team felt this was problematic and "nobody would ever actually want to do that." We were wrong, and due to many user requests, you can now opt in to scanning .exe files with a new public method on AssemblyScanner shown below:


[Fact]
public void scan_all_assemblies_in_a_folder_including_exe()
{
    Scan(x => x.AssembliesAndExecutablesFromPath(assemblyScanningFolder));

    shouldHaveFamilyWithSameName<IInterfaceInWidget5>();
    shouldHaveFamilyWithSameName<IWorker>();
    shouldHaveFamilyWithSameName<IDefinedInExe>();
}


[Fact]
public void scan_all_assemblies_in_application_base_directory_including_exe()
{
    Scan(x => x.AssembliesAndExecutablesFromApplicationBaseDirectory());

    shouldHaveFamilyWithSameName<IInterfaceInWidget5>();
    shouldHaveFamilyWithSameName<IWorker>();
    shouldHaveFamilyWithSameName<IDefinedInExe>();
}


Do be aware that while this technique is very powerful for extensibility, it's been extremely problematic for some folks in the past. The StructureMap team's recommendation for using this feature is to:

  1. Make sure you have some kind of filter on the assemblies scanned for performance and predictability reasons. Either a naming convention or filter by an assembly attribute to narrow where StructureMap looks
  2. Get familiar with the new type scanning diagnostics introduced in 4.0;-)

Behind the scenes, StructureMap is using the Assembly.GetExportedTypes() method from the .Net CLR to find types and this mechanism is very sensitive to missing dependencies. Again, thanks to the new type scanning diagnostics, you now have some visibility into assembly loading failures that used to be silently swallowed internally.

Excluding Types

StructureMap also makes it easy to exclude types, either individually or by namespace. The following examples also show how StructureMap can register an assembly by providing a type within that assembly.

Excluding additional types or namespaces is as easy as calling the corresponding method again.


[Fact]
public void use_a_single_exclude_of_type()
{
    Scan(x =>
    {
        x.AssemblyContainingType<ITypeThatHasAttributeButIsNotInRegistry>();
        x.ExcludeType<ITypeThatHasAttributeButIsNotInRegistry>();
    });

    shouldHaveFamily<IInterfaceInWidget5>();
    shouldNotHaveFamily<ITypeThatHasAttributeButIsNotInRegistry>();
}

[Fact]
public void use_a_single_exclude2()
{
    Scan(x =>
    {
        x.AssemblyContainingType<ITypeThatHasAttributeButIsNotInRegistry>();
        x.ExcludeNamespace("StructureMap.Testing.Widget5");
    });

    shouldNotHaveFamily<IInterfaceInWidget5>();
    shouldNotHaveFamily<ITypeThatHasAttributeButIsNotInRegistry>();
}

[Fact]
public void use_a_single_exclude3()
{
    Scan(x =>
    {
        x.AssemblyContainingType<ITypeThatHasAttributeButIsNotInRegistry>();
        x.ExcludeNamespaceContainingType<ITypeThatHasAttributeButIsNotInRegistry>();
    });

    shouldNotHaveFamily<IInterfaceInWidget5>();
    shouldNotHaveFamily<ITypeThatHasAttributeButIsNotInRegistry>();
}


Custom Registration Conventions

It's just not possible (or desirable) for StructureMap to include every possible type of auto registration convention users might want, but that's okay because StructureMap allows you to create and use your own conventions through the IRegistrationConvention interface:


public interface IRegistrationConvention
{
    void ScanTypes(TypeSet types, Registry registry);
}

Let's say that you'd like a custom convention that just registers a concrete type against all the interfaces that it implements. You could then build a custom IRegistrationConvention class like the following example:


public interface IFoo
{
}

public interface IBar
{
}

public interface IBaz
{
}

public class BusyGuy : IFoo, IBar, IBaz
{
}

// Custom IRegistrationConvention
public class AllInterfacesConvention : IRegistrationConvention
{
    public void ScanTypes(TypeSet types, Registry registry)
    {
        // Only work on concrete types
        types.FindTypes(TypeClassification.Concretes | TypeClassification.Closed).Each(type =>
        {
            // Register against all the interfaces implemented
            // by this concrete class
            type.GetInterfaces().Each(@interface => registry.For(@interface).Use(type));
        });
    }
}

[Fact]
public void use_custom_registration_convention()
{
    var container = new Container(_ =>
    {
        _.Scan(x =>
        {
            // You're probably going to want to tightly filter
            // the Type's that are applicable to avoid unwanted
            // registrations
            x.TheCallingAssembly();
            x.IncludeNamespaceContainingType<BusyGuy>();

            // Register the custom policy
            x.Convention<AllInterfacesConvention>();
        });
    });

    container.GetInstance<IFoo>().ShouldBeOfType<BusyGuy>();
    container.GetInstance<IBar>().ShouldBeOfType<BusyGuy>();
    container.GetInstance<IBaz>().ShouldBeOfType<BusyGuy>();
}


The Default ISomething/Something Convention

The "default" convention simply tries to connect concrete classes to interfaces using the I[Something]/[Something] naming convention as shown in this sample:


public interface ISpaceship { }

public class Spaceship : ISpaceship { }

public interface IRocket { }

public class Rocket : IRocket { }

[Fact]
public void default_scanning_in_action()
{
    var container = new Container(_ =>
    {
        _.Scan(x =>
        {
            x.Assembly("StructureMap.Testing");
            x.WithDefaultConventions();
        });
    });

    container.GetInstance<ISpaceship>().ShouldBeOfType<Spaceship>();
    container.GetInstance<IRocket>().ShouldBeOfType<Rocket>();
}


The StructureMap team contains some VB6 veterans who hate Hungarian Notation, but can't shake the "I" nomenclature.

Registering the Single Implementation of an Interface

To tell StructureMap to automatically register any interface that only has one concrete implementation, use this method:


public interface ISong { }

public class TheOnlySong : ISong { }

[Fact]
public void only_implementation()
{
    var container = new Container(_ =>
    {
        _.Scan(x =>
        {
            x.TheCallingAssembly();
            x.SingleImplementationsOfInterface();
        });
    });

    container.GetInstance<ISong>()
        .ShouldBeOfType<TheOnlySong>();
}


Register all Concrete Types of an Interface

To add all concrete types that can be cast to a named plugin type, use this syntax:


public interface IFantasySeries { }

public class WheelOfTime : IFantasySeries { }

public class GameOfThrones : IFantasySeries { }

public class BlackCompany : IFantasySeries { }

[Fact]
public void register_all_types_of_an_interface()
{
    var container = new Container(_ =>
    {
        _.Scan(x =>
        {
            x.TheCallingAssembly();

            x.AddAllTypesOf<IFantasySeries>();

            // or

            x.AddAllTypesOf(typeof(IFantasySeries))
                .NameBy(type => type.Name.ToLower());
        });
    });

    container.Model.For<IFantasySeries>()
        .Instances.Select(x => x.ReturnedType)
        .OrderBy(x => x.Name)
        .ShouldHaveTheSameElementsAs(typeof(BlackCompany), typeof(GameOfThrones), typeof(WheelOfTime));
}


Note, "T" does not have to be an interface, it's all based on the ability to cast a concrete type to the "T"

Generic Types

See Generic Types for an example of using the ConnectImplementationsToTypesClosing mechanism for generic types.

Register Concrete Types against the First Interface

The last built in registration convention is a mechanism to register all concrete types that implement at least one interface against the first interface that they implement.


container = new Container(x =>
{
    x.Scan(o =>
    {
        o.TheCallingAssembly();
        o.RegisterConcreteTypesAgainstTheFirstInterface();

        o.Exclude(t => t.CanBeCastTo(typeof(IGateway)));
    });
});