Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structuremap - How to register certain types in certain layers

I am using the Registry DSL example to configure structuremap. But doing this makes all of my registered types available in all layers of my application where I add a refernce to structure map. I dont want my business layer to know anything about my data access layer and vice versa. How do I get structuremap to only register specific types for each of my layers?

Here is the code in my global.asax file:

ObjectFactory.Initialize(x =>
{
  x.AddRegistry<RegistryIOC>();
});

And here is my RegistryIOC class:

public class RegistryIOC : SMRegistry
{

    public RegistryIOC() 
    {
        For<IProfileService>.Use<ProfileService>();
        For<IProctorService>().Use<ProctorService>();

        //Business Logic Objects
        For<IQual>().Use<Qual>();
        For<ITest>().Use<Test>();
        For<IBoldface>().Use<Boldface>();
        For<ITrainingPlan>().Use<TrainingPlan>();
        For<IUnit>().Use<Unit>();

        //Data Transfer Objects
        For<IGenericDTO>().Use<GenericDTO>();
        For<IProfileDTO>().Use<ProfileDTO>();
        For<IQualDTO>().Use<QualDTO>();
        For<IPermissionDTO>().Use<PermissionDTO>();

        //Repository Objects
        For<IProctorRepository>().Use<ProctorRepository>();
        For<IQualsRepository>().Use<QualsRepository>();
        For<ITestRepository>().Use<TestRepository>();
        For<IUnitRepository>().Use<UnitRepository>();
        For<IUserRepository>().Use<UserRepository>();
    }

}

Thanks for the help.

like image 803
Colin Pear Avatar asked Nov 04 '11 18:11

Colin Pear


1 Answers

I am using reflection to accomplish this (and other) tasks. Let me show how that works.

The first thing to do is to define an interface that allows us to identify classes that perform initialization tasks:

public interface IConfigurationTask
{
    void Configure();
}

Next, create one or more classes that implement this interface. These classes will be spread across all of your projects, which is another way of saying that you can put them "where they belong".

public class RepositoryInitializer : IConfigurationTask
{
    public void Configure()
    {
        // code that does relevant initialization goes here
    }
}

The last piece of the puzzle is to find classes that implement the IConfigurationTask interface, create an instance of them and execute the Configure method. This is the purpose of the ConfigurationTaskRunner:

public static class ConfigurationTaskRunner
{
    public static void Execute( params string[] assemblyNames )
    {
        var assemblies = assemblyNames.Select( Assembly.Load ).Distinct().ToList();
        Execute( assemblies );
    }

    public static void Execute( IEnumerable<Assembly> assemblies )
    {
        var tasks = new List<IConfigurationTask>();
        assemblies.ForEach( a => tasks.AddRange( a.CreateInstances<IConfigurationTask>() ) );

        tasks.ForEach( t => t.Configure() );
    }
}

The code shown here uses a custom extension to iterate over all items in a list and execute an action for every item (the ForEach method). I am also using a reflection library to make the task of locating and instantiating the instances a one-liner (the CreateInstances method), but you could achieve the same using just plain reflection (as shown in the code below).

public static IList<T> CreateInstances<T>( this Assembly assembly )
{
     var query = from type in assembly.GetTypes().Where( t => typeof(T).IsAssignableFrom( t ) && typeof(T) != t ) 
                 where type.IsClass && ! type.IsAbstract && type.GetConstructor( Type.EmptyTypes ) != null 
                 select (T) Activator.CreateInstance( type );
     return query.ToList();
}    

The final piece of the puzzle is to trigger the execution of the ConfigurationTaskRunner. For example, in a web application this would go into Application_Start in Global.asax:

// pass in the names of the assemblies we want to scan, hardcoded here as an example 
ConfigurationTaskRunner.Execute( "Foo.dll", "Foo.Domain.dll" );

I've also found it useful with a derived IPrioritizedConfigurationTask (that adds a Priority property) to allow proper ordering of the tasks before you execute them. This is not shown in the example code above, but is fairly trivial to add.

Hope this helps!

like image 52
Morten Mertner Avatar answered Oct 18 '22 03:10

Morten Mertner