May 7, 2014

Register Instances with StructureMap Automatically

Post by: Dan Lorenz

StructureMap is an IoC, Inversion of Control, solution usable in .NET. IoC is used to prevent hard links between classes through the use of Interfaces. By coding against an interface, you are able to reuse and test more code. You can also dynamically change what concrete class [a class that can be “newed up”] is actually being called, changing the code that runs without having to rebuild and DLLs.

However, one of the problems doing this is to remember to register all the concrete classes to use for all of the interfaces. Normally, you have to tell StructureMap (or any IoC solution) that for this Interface, use this concrete implementation. Thus, if you start having dozens of classes that need to be registered, you have to take a bit of time to go set that up.  If you start having hundreds, it can start to become a headache. (See our app modernization capabilities)

Instead, there is a way to automatically register any interface with the concrete version of that class. You can write the code once and never have to worry about registering those interface/concrete classes again, assuming there is no special logic. We can use reflection to pull out all concrete classes from an assembly, get all the interfaces that concrete class implements, invoke the constructor to create the object, and register that object with StructureMap.

Obviously, you wouldn’t want to register interfaces on all the classes you find in a project. Thus, you can create an IStructureMapClass interface that has no methods or properties on it. Any concrete class that has this interface can be pulled out of the assembly and auto-registered.


    public interface IStructureMapClass
    {

    }


From there, you can put this interface on any concrete class you want to automatically register and invoke the default constructor.

For example, let’s say you have a ProductService class you want to auto register. This class implements the IProductService interface and inherits from ServiceBase. You can put the IStructureMapClass on ProductService or even ServiceBase if you want all Services to be auto-registered:



public interface IProductService
    {
        int Save(Product entity);
    }

    public class ServiceBase : IStructureMapClass
    {

    }

    public class ProductService : ServiceBase, IProductService
    {

        public int Save(Product entity)
        {
            int affectedRows = 0;
            // Do save call
            return affectedRows;
        }
    }




        protected void Application_Start()
        {
            StructureMap.ObjectFactory.Initialize(x =>
            {
                x.AddRegistry();
            });
        }   


The StructureMapInitialize class is another class in the same project that is responsible for what assemblies to look in. This class could look something like this


public class StructureMapInitialize : StructureMap.Configuration.DSL.Registry
    {
        public StructureMapInitialize()
        {
            Scan(x =>
            {
                x.Assembly(ConfigurationManager.AppSettings["ServiceAssembly"]);
                x.Assembly(ConfigurationManager.AppSettings["RepositoryAssembly"]);             
                x.LookForRegistries();
            });
        }
    }

This will pull out the name of the assembly to look for from the config file. Now we can dynamically change what DLLs to use without having to rebuild the entire solution. We can just update the config file.

Now we need to create the Registry class in the Service Assembly (and Repository Assembly) that StructureMap will look for. It will look something like this:


    public class StructureMapInitialize : StructureMap.Configuration.DSL.Registry
    {
        public StructureMapInitialize()
        {
        }

    }
 


Now this is where the reflection will come into play. This constructor will be called once and only once to register all the classes you want StructureMap to work with. To make the classes automatically register themselves, we can use reflection like so: 



public class StructureMapInitialize : StructureMap.Configuration.DSL.Registry
    {
        public StructureMapInitialize()
        {
            // Register all classes the implement the IStructureMapClass and register that interface for all classes
            Type structureMapCheck = typeof(IStructureMapClass);
            foreach (var type in Assembly.GetAssembly(typeof(StructureMapInitialize)).GetTypes().Where(a => !a.IsAbstract))
            {
                if (structureMapCheck.IsAssignableFrom(type))
                {
                    var defaultConstructor = type.GetConstructors().Where(a => a.GetParameters().Count() == 0).FirstOrDefault();
                    if (defaultConstructor != null)
                    {
                        dynamic instance = defaultConstructor.Invoke(null);

                        var interfaces = type.GetInterfaces();
                        foreach (var item in interfaces)
                        {
                            if (item != structureMapCheck)
                                For(item).Use(instance);
                        }
                    }
                }
            }
        }
    }

The first thing we do is to get the type of IStructureMapClass. This will be what we are checking for. However, we don’t want to register IStructureMapClass with any concrete types because everything would match! Thus, we filter it out later on. 

Second, we get all of the classes that are in this assembly and loop through them. If the class is assignable from IStructureMapClass, we know we need to register that class. 

Third, we pull out the default constructor of the class. If you wanted to, you could get fancy and use some attributes on your class if you needed to pass in some sort of parameters to a specific constructor. Otherwise, we will just assume we want to invoke the default constructor.

Fourth, if the default constructor exists, we invoke it, storing the resulting object in the instance variable.

Last, we need to loop through all of the interfaces this class has implemented and register them with StructureMap. We loop through each interface and register the object returned from the default constructor with each one, except IStructureMapClass.

Now, every time you add a new class that you want StructureMap to pick up, you just have it implement IStructureMapClass. You don’t have to worry about maintaining a huge list of Interface/Concrete class implementations anymore.

Subscribe to our Newsletter

Stay informed on the latest technology news and trends

Relevant Insights

Should You Disrupt Yourself to Accelerate Digital Transformation?

It has been interesting to watch Microsoft transition from a company that makes its money via licensing to one that...

Cybersecurity Myth Busted: Tools Are the Solution

When thinking about security, people often gravitate towards implementing various security tools, solutions, or products. If you bring up a...

Time to Reconsider MP-BGP EVPN for Your Datacenter Network?

VxLAN was defined in 2014 by RFC 7348 and has been used as a component in several SDN (software defined...
X