StructureMap has always had some ability to register types by by scanning assemblies (auto registration).  The original version of StructureMap circa 2004 looked for types decorated with specific attributes.  Version 2.0 added the ability to look for Registry classes within those assemblies.  The auto registration mechanism has been completely revamped in StructureMap 2.5 with greatly extended capabilities.  The downside is that the "scanning" syntax in v2.5 is completely incompatible with previous versions, including the 2.4.9 preview release.  However, the Xml configuration usage has remained consistent in both form and function.

Using the Scan() Expression

Auto registration begins with the Scan() expression inside a Registry class or the now deprecated StructureMapConfiguration class.  Here's an example from my current project:

            Scan(x =>

            {

                x.TheCallingAssembly();

 

                x.ExcludeNamespaceContainingType<IEvent>();

                x.ExcludeNamespaceContainingType<SearchModel>();

                x.ExcludeNamespaceContainingType<AuthenticationService>();

                x.ExcludeNamespaceContainingType<DovetailController>();

 

                x.AddAllTypesOf<IDomainMap>();

 

                x.WithDefaultConventions();

                x.With<DomainEntityAliaser>();

            });

The signature of the Scan() method is this:

        public void Scan(Action<IAssemblyScanner> action)

        {

        }

The actual work is performed by an IAssemblyScanner object.  The Scan() method uses the Nested Closure pattern as a way of configuring and registering the IAssemblyScanner in a single atomic action.  Each Scan() expression is a completely self-contained, atomic action.  This is a significant change from the earlier functionality in versions 2.0 and 2.4.9.  This breaking change was made to reduce confusion by making the actual functionality more closely match the DSL expression. 

The scanner itself will not be executed until near the end of the configuration process.  Be aware of this issue if you are trying to debug custom ITypeScanner classes.

The IAssemblyScanner interface exposes these directives to control the assembly scanning:

    public interface IAssemblyScanner

    {

        // Determining which assemblies to scan

        void Assembly(Assembly assembly);

        void Assembly(string assemblyName);

        void TheCallingAssembly();

        void AssemblyContainingType<T>();

        void AssemblyContainingType(Type type);

 

        // Adding conventions

        void With(ITypeScanner scanner);

        void WithDefaultConventions();

        void With<T>() where T : ITypeScanner, new();

 

        // Other options

        void LookForRegistries();

        void AddAllTypesOf<PLUGINTYPE>();

        void AddAllTypesOf(Type pluginType);

        void IgnoreStructureMapAttributes();

 

        // Filtering the types that will be scanned

        void Exclude(Predicate<Type> exclude);

        void ExcludeNamespace(string nameSpace);

        void ExcludeNamespaceContainingType<T>();

        void Include(Predicate<Type> predicate);

        void IncludeNamespace(string nameSpace);

        void IncludeNamespaceContainingType<T>();

        void ExcludeType<T>();

    }

Roughly put, these are the features and options of each individual Scan() expression:

  1. Analyzing all the exported types in the specified Assembly's
  2. By default, looking for all types decorated with either the [PluginFamily] or [Pluggable] attributes.  This feature can be disabled by calling the IAssemblyScanner.IgnoreStructureMapAttributes() method.  See NEED A LINK HERE for more information
  3. If explicitly specified with the use of the LookForRegistries() method, the scanning will apply the configuration contained within any concrete Registry class found in the assemblies being scanned.
  4. Optionally, the scanning can automatically add any concrete types found that can be "plugged" into a given PluginType
  5. The scanning of types within an Assembly can be further filtered by specifying rules to either include or exclude types based on Predicate<Type> rules.
  6. Lastly, custom scanning conventions and policies can be added and used from the Scan() expression. 

Rules for Auto Registering a Type

If you are interested in using the Scan() expression for auto registration, the first thing to know is how StructureMap determines whether or not a given concrete type should be registered. 


Designating Assemblies

The first thing you need to do to utilize the type scanning is to designate which assemblies to scan.  The type scanning will throw an exception if you attempt to call the Scan() expression without designating at least one assembly.  Use one of the following methods on IAssemblyScanner to designate assemblies:

    public interface IAssemblyScanner

    {

        void Assembly(Assembly assembly);

        void Assembly(string assemblyName);

        void TheCallingAssembly();

        void AssemblyContainingType<T>();

        void AssemblyContainingType(Type type);

        void AssembliesFromPath(string path);

        void AssembliesFromPath(string path, Predicate<Assembly> assemblyFilter);

 

        // ... Other methods

    }

Adding an assembly directly:

    public class ScanningRegistry : Registry

    {

        public ScanningRegistry()

        {

            Scan(x =>

            {

                // Add assembly by name.

                x.Assembly("StructureMap.Testing.Widget");

 

                // Add an assembly directly

                x.Assembly(Assembly.GetExecutingAssembly());

 

                // Add the assembly that contains a certain type

                x.AssemblyContainingType<IService>();

                // or

                x.AssemblyContainingType(typeof(IService));

            });

        }

    }

The most common usage is probably just specifying "this" assembly with IAssemblyScanner.TheCallingAssembly():

    public class WebCoreRegistry : Registry

    {

        protected override void configure()

        {

            ForRequestedType<ICachedSet>().TheDefaultIsConcreteType<CachedSet>().CacheBy(InstanceScope.Hybrid);

            ForRequestedType<IControlBuilder>().Use<AspNetControlBuilder>();

            ForRequestedType<IPartialRenderer>().Use<PartialRenderer>();

 

            Scan(x =>

            {

                // Scan "this" assembly.  In other words, the assembly that

                // contains the WebCoreRegistry class

                x.TheCallingAssembly();

 

                x.IncludeNamespaceContainingType<AuthenticationContext>();

                x.IncludeNamespaceContainingType<ISecurityDataService>();

 

                x.WithDefaultConventions();

            });

        }

    }


Scan all Assemblies in a Given Folder

StructureMap 2.5 has a brand new capability to auto register types in all assemblies in a given folder path.  My current project is using this feature for our extensibility mechanism.  For customer-specific deployments, we need to add business rules and even all new screens and features to our application that can be discovered at runtime -- without changing our core code.  Our design calls for all extensions to be created in separate assemblies.  On application startup, our system will look for all assemblies in a well known folder and use StructureMap to scan these assemblies for the extensions that will be specified in Registry classes.  Our bootstrapping looks like:

            Scan(x =>

            {

                // I'm telling StructureMap to sweep a folder called "Extensions" directly

                // underneath the application root folder for any assemblies found in that folder

                x.AssembliesFromPath("Extensions");/p>

 

                // I also direct StructureMap to add any Registries that it finds in these

                // assemblies.  I'm assuming that all the StructureMap directives are

                // contained in Registry classes -- and this is the recommended approach

                x.LookForRegistries();

            });

Note in the code above that I made an explicit call to "LookForRegistries."  Scanning for Registry's is no longer active by default.  This is a breaking change from the 2.4.9 preview release. 

You can also filter the assemblies based on a Predicate like this:

            Scan(x =>

            {

                // This time I'm going to specify a filter on the assembly such that

                // only assemblies that have "Extension" in their name will be scanned

                x.AssembliesFromPath("Extensions", assembly => assembly.GetName().Name.Contains("Extension"));

 

                x.LookForRegistries();

            });


Add All Concrete Types for a PluginType

Sometimes you may simply want to automatically add all the concrete classes that implement or inherit from a given PluginType.  In my current project we are using StructureMap to bootstrap and configure NHibernate.  We're also using Fluent NHibernate for our mapping configuration.  The mapping configuration is specified in classes that implement an interface named IDomainMap.  To configure NHibernate, we need to sweep our core assembly to find all concrete implementations of IDomainMap and execute the mappings on each type.  We have a Registry called "CoreRegistry" that configures NHibernate (among other things).  We specify the auto registration of all the IDomainMap types like this:

            Scan(x =>

            {

                // Scan the types in the assembly that contains this Registry class

                x.TheCallingAssembly();

 

                // Finds and adds all concrete implementations of the IDomainMap

                // interface in the targetted assemblies

                x.AddAllTypesOf<IDomainMap>();

            });

After running the Scan() statement above, we can retrieve an instance of all the IDomainMap types by calling:

 

            // Retrieve all registered IDomainMap objects

            IList<IDomainMap> allMaps = ObjectFactory.GetAllInstances<IDomainMap>();

 

More appropriately in our code, we have a "Query" subsystem that also uses the IDomainMap objects for its metadata.  We have a class called EntityQueryRepository that "knows" how each Domain Model class is queried.  When the EntityQueryRepository object is created, it takes in an array of IDomainMap objects.

        // Unless explicitly configured otherwise, when StructureMap

        // sees a constructor or setter argument that is an array,

        // it will fill this argument with all of the instances

        // of the argument type -- in this case, all the registered

        // IDomainMap objects

        public EntityQueryRegistry(IDomainMap[] maps)

        {

            maps.Each(m => m.RegisterQueryModel(this));

        }


Excluding or Including Types

It's quite possible that you may only want to scan part of an assembly within a single Scan() operation.  You can specify the scope of the Scan() operation within a given assembly by either including or excluding types based on a Predicate<Type> function.  For convenience sake, IAssemblyScanner directly exposes some of the more likely options.  The methods for filtering types are:

    public interface IAssemblyScanner

    {

        void Exclude(Predicate<Type> exclude);

        void ExcludeNamespace(string nameSpace);

        void ExcludeNamespaceContainingType<T>();

        void Include(Predicate<Type> predicate);

        void IncludeNamespace(string nameSpace);

        void IncludeNamespaceContainingType<T>();

        void ExcludeType<T>();

 

        // ... Other methods

    }

The "Include" and "Exclude" methods work independently of each other.  If any "include" rules are set, a type must pass one or more of the "include" rules before it will be processed.  If any "exclude" rules are designated, any type that matches one or more of these Predicate<Type> rules will not be processed.  The recommendation is to not mix and match includes and excludes in the same Scan() method.

The methods that work on namespaces are doing a string match against the namespace of a class like this:

    public static class TypeExtensions

    {

        public static bool IsInNamespace(this Type type, string nameSpace)

        {

            return type.Namespace.StartsWith(nameSpace);

        }

    }

Finally, here's an example of the type filtering in action from my current project (in other words, the scenario that forced us to overhaul the StructureMap auto registration):

    public class CoreRegistry : Registry

    {

        protected override void configure()

        {

            Scan(x =>

            {

                // Scan the types in the assembly that contains this Registry class

                x.TheCallingAssembly();

 

                // The "Core" of our application does NOT include

                // these namespaces that are specific to either

                // the user interface or satellite services

                x.ExcludeNamespaceContainingType<IEvent>();

                x.ExcludeNamespaceContainingType<SearchModel>();

                x.ExcludeNamespaceContainingType<AuthenticationService>();

                x.ExcludeNamespaceContainingType<DovetailController>();

 

                // Specify some scanning conventions

                x.AddAllTypesOf<IDomainMap>();

                x.WithDefaultConventions();

 

                // scan with a custom ITypeScanner

                x.With<DomainEntityAliaser>();

            });

        }


Type Scanning with Attributes

A subset of StructureMap functionality can be configured through the usage of attributes.  Please see Using Attributes for more information.


Convention Based Type Registration

StructureMap 2.5+ adds the ability to do convention based type registration.  Convention over Configration is becoming increasingly more common as a way to reduce the overhead of using configuration intensive tools.  Type registration conventions can be mixed and matched with explicit configuration.

The Default Convention

When using StructureMap over the last couple years I saw a lot of type registrations like:

            Start<ICommandExecutor>().TheDefaultIsConcreteType<CommandExecutor>();

There was a reoccuring pattern.  "Foo" was the default implementation of "IFoo."  The "I" moniker is the last vestige of Hungarian Notation left in my coding style, but it refuses to go away, so let's just take advantage of that naming convention with a new DefaultConvention. 

Let's say that we have a class and interface like:

    public interface IConvention

    {

    }

 

    public class Convention : IConvention

    {

    }

To make Convention be the default type for IConvention, I can simply scan the assembly containing these types with the new default convention:

            var container = new Container(registry =>

            {

                registry.Scan(x =>

                {

                    x.TheCallingAssembly();

                    x.With<DefaultConventionScanner>();

                });

            });

 

            container.GetInstance<IConvention>().ShouldBeOfType<Convention>();


Registering Types by Name

One of the simplest, but yet most useful, ways to use convention based auto registration is just to add all concrete types that can be cast to a certain PluginType and use a convention to determine the Instance name.  The first place I used this functionality was at the beginning of our MVC project.  Let's say that we name our Controller classes like this:

    public interface IController{}

    public class AddressController : IController{}

    public class SiteController : IController{}

As a convention, we'd like to register AddressController as the "Address" of IController, and "Site" or IController like this:

            container = new Container(x =>

            {

                x.ForRequestedType<IController>().AddInstances(o =>

                {

                    o.OfConcreteType<AddressController>().WithName("Address");

                    o.OfConcreteType<SiteController>().WithName("Site");

                })

            });

Over 50+ Controller classes, that coding is going to get extremely tedious.  Let's use a convention instead:

            container = new Container(x =>

            {

                x.Scan(o =>

                {

                    o.TheCallingAssembly();

                    o.AddAllTypesOf<IController>().NameBy(type => type.Name.Replace("Controller", ""));

                });

            });

 

            foreach (var instance in container.Model.InstancesOf<IController>())

            {

                Debug.WriteLine(instance.Name + " is " + instance.ConcreteType.Name);

            }

When this code is executed, we get this output:

Address is AddressController
Site is SiteController

Here's the unit test code for this functionality:

        [Test]

        public void can_find_objects_later_by_name()

        {

            container.GetInstance<IController>("Address")

                .ShouldBeOfType<AddressController>();

 

            container.GetInstance<IController>("Site")

                .ShouldBeOfType<SiteController>();

        }


Creating and Using Your Own Convention

To write your own auto registration convention, write a concrete class that implements the ITypeScanner interface:

    public interface ITypeScanner

    {

        void Process(Type type, PluginGraph graph);

    }

Inside a custom ITypeScanner, you add types to a Container by modifying the PluginGraph object that is passed into the TypeScanner.  While having access to the PluginGraph allows you to make any possible type of configuration change (PluginGraph is the Semantic Model behind both the Registry DSL and the Xml configuration), in practice you probably only care about these three methods:

        /// <summary>

        /// Adds the concreteType as an Instance of the pluginType

        /// </summary>

        /// <param name="pluginType"></param>

        /// <param name="concreteType"></param>

        void AddType(Type pluginType, Type concreteType);

 

        /// <summary>

        /// Adds the concreteType as an Instance of the pluginType with a name

        /// </summary>

        /// <param name="pluginType"></param>

        /// <param name="concreteType"></param>

        /// <param name="name"></param>

        void AddType(Type pluginType, Type concreteType, string name);

 

        /// <summary>

        /// Add the pluggedType as an instance to any configured pluginType where pluggedType

        /// could be assigned to the pluginType

        /// </summary>

        /// <param name="pluggedType"></param>

        void AddType(Type pluggedType);

Here's a sample type convention from my current project:

    /// <summary>/span>

    /// This TypeScanner looks for any concrete class that implements

    /// an IFlattenerFor<T> interface, and registers that type

    /// against the closed IFlattenerFor<T> interface,

    /// i.e. IFlattenerFor<Address> or IFlattenerFor<Site>

    /// </summary>

    public class DtoFlattenerConventionScanner : ITypeScanner

    {

        public void Process(Type type, PluginGraph graph)

        {

            Type interfaceType = type.FindInterfaceThatCloses(typeof (IFlattenerFor<>));

            if (interfaceType != null)

            {

                graph.AddType(interfaceType, type);

            }

        }

    }

You apply the custom ITypeScanner by simply calling With() inside of a Scan() block:

            Scan(x =>

            {

                // scan with a custom ITypeScanner

                x.With<DomainEntityAliaser>();

                x.With<QueueItemMappingScanner>();

            });


Looking for Registries

A significant change from StructureMap 2.0 to 2.5 is that automatically scanning for Registry classes in assemblies is no longer the default option.  In order to use any Registry classes found in the type scanning as part of the configuration, use this syntax:

            Scan(x =>

            {

                // I'm telling StructureMap to sweep a folder called "Extensions" directly

                // underneath the application root folder for any assemblies

                x.AssembliesFromPath("Extensions");

 

                // I also direct StructureMap to add any Registries that it finds in these

                // assemblies.  I'm assuming that all the StructureMap directives are

                // contained in Registry classes -- and this is the recommended approach

                x.LookForRegistries();

            });