views:

34

answers:

2

If I have an interface:

public interface IRepository<T>

And an abstract class:

public abstract class LinqToSqlRepository<T, TContext> : IRepository<T>
        where T : class
        where TContext : DataContext

And a whole bunch of implementations of IRepository / LinqToSqlRepository (e.g. AccountRepository, ContactRepository, etc.), what's the best way to to use StructureMap (2.5.3) to generically wire them all up?

e.g., I want this code to pass:

[Test]    
public void ShouldWireUpAccountRepositories
{
  var accountRepo = ObjectFactory.GetInstance<IRepository<Account>>();
  Assert.IsInstanceOf<AccountRepository>(accountRepo);
}

Without explicitly writing this:

ObjectFactory.Configure(x => x.ForRequestedType<IRepository<Account>>()
    .TheDefaultIsConcreteType<AccountRepository>());

In the past, we've always created a specific interface on each repository that inherited from the generic one, and used the default scanner to automatically wire all of those instances, but I'd like to be able to ask specifically for an IRepository<Account> without cluttering up the project with additional interfaces / configurations.

+1  A: 

Using Fasterflect you could write the following code:

// get the assembly containing the repos
var assembly = Assembly.GetExecutingAssembly();
// get all repository types (classes whose name end with "Repository")
var types = assembly.Types( Flags.PartialNameMatch, "Repository" ).Where( t => t.IsClass );
// configure StructureMap for the found repos
foreach( Type repoType in types )
{
    Type entityType = assembly.Type( repoType.Name.Replace( "Repository", "" );
    // define the generic interface-based type to associate with the concrete repo type
    Type genericRepoType = typeof(IRepository).MakeGenericType( entityType );
    ObjectFactory.Configure( x => x.For( RequestedType( genericRepoType ) ).Use( repoType ) );
}

Note that the above is written from memory and has not been compiler verified. You'll need the source version of Fasterflect to make it compile.

Morten Mertner
1) You never check that repoType implements genericRepoType. 2) You should not call ObjectFactory.Configure() in a loop, if you can help it. StructureMap's Scan() and IRegistrationConvention features are designed for this purpose.
Joshua Flanagan
@Joshua: good points - I'm clearly no StructureMap expert :)
Morten Mertner
+2  A: 

StructureMap's scanning feature can handle that:

ObjectFactory.Initialize(x => {
  x.Scan(y =>
  {
      y.TheCallingAssembly();
      y.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
    });
});
Joshua Flanagan