Fork me on GitHub

Type Scanning Diagnostics Edit on GitHub


Type scanning and conventional auto-registration is a very powerful feature in StructureMap, but it has been frequently troublesome to users when things go wrong. To try to alleviate problems, StructureMap 4.0 introduces some new functionality for detecting and diagnosing problems with type scanning, mostly related to Assembly's being missing.

Assert for Assembly Loading Failures

At its root, most type scanning and auto-registration schemes in .Net frameworks rely on the Assembly.GetExportedTypes() method. Unfortunately, that method can be brittle and fail whenever any dependency of that Assembly cannot be loaded into the current process, even if your application has no need for that dependency. In StructureMap 3.* and before, that loading exception was essentially swallowed and lost. In StructureMap 4 and above, you can use this method to assert the presence of any assembly load exceptions during type scanning:


TypeRepository.AssertNoTypeScanningFailures();

The method above will throw an exception listing all the Assembly's that failed during the call to GetExportedTypes() only if there were any failures. Use this method during your application bootstrapping if you want it to fail fast with any type scanning problems.

What did StructureMap scan?

Confusion of type scanning has been a constant problem with StructureMap usage over the years -- especially if users are trying to dynamically load assemblies from the file system for extensibility. In order to see into what StructureMap has done with type scanning, 4.0 introduces the Container.WhatDidIScan() method.

Let's say that you have a Container that is set up with at least two different scanning operations like this sample from the StructureMap unit tests:


var container = new Container(_ =>
{
    _.Scan(x =>
    {
        x.TheCallingAssembly();

        x.WithDefaultConventions();
        x.RegisterConcreteTypesAgainstTheFirstInterface();
        x.SingleImplementationsOfInterface();
    });

    _.Scan(x =>
    {
        // Give your scanning operation a descriptive name
        // to help the diagnostics to be more useful
        x.Description = "Second Scanner";

        x.AssembliesFromApplicationBaseDirectory(assem => assem.FullName.Contains("Widget"));
        x.ConnectImplementationsToTypesClosing(typeof(IService<>));
        x.AddAllTypesOf<IWidget>();
    });
});

Debug.WriteLine(container.WhatDidIScan());

The resulting textual report is shown below:

Sorry for the formatting and color of the text, but the markdown engine does not like the textual report


/*
All Scanners
================================================================
Scanner #1
Assemblies
----------
* StructureMap.Testing, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null

Conventions
--------
* Default I[Name]/[Name] registration convention
* Register all concrete types against the first interface (if any) that they implement
* Register any single implementation of any interface against that interface

Second Scanner
Assemblies
----------
* StructureMap.Testing.GenericWidgets, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
* StructureMap.Testing.Widget, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null
* StructureMap.Testing.Widget2, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null
* StructureMap.Testing.Widget3, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null
* StructureMap.Testing.Widget4, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null
* StructureMap.Testing.Widget5, Version=4.0.0.51243, Culture=neutral, PublicKeyToken=null

Conventions
--------
* Connect all implementations of open generic type IService<T>
* Find and register all types implementing StructureMap.Testing.Widget.IWidget

*/

The textual report will show:

  1. All the scanning operations (calls to Registry.Scan()) with a descriptive name, either one supplied by you or the Registry type and an order number.
  2. All the assemblies that were part of the scanning operation including the assembly name, version, and a warning if Assembly.GetExportedTypes() failed on that assembly.
  3. All the configured scanning conventions inside of the scanning operation

WhatDidIScan() does not at this time show any type filters or exclusions that may be part of the assembly scanner.

See also: Auto-Registration and Conventions