Registering Types with an Interface that Derives from an Open Generic Type in StructureMap January, 2010
Jimmy Bogard has written a couple of posts here and here discussing the registering partially closed generic types with StructureMap. These posts helped me solve a problem I was having which is a little bit different. To set the stage let me explain what we're doing. First we have a generic repository interface:
public interface IRepository: IQueryable where TEntity : class { TEntity Get(Guid id); void Save(TEntity entity); void Delete(TEntity entity); void DeleteSingle(IQueryable query); void DeleteMany(IQueryable query); }
Then we will have an interface for a specific repository that inherits from the generic repository interface and includes convenience methods:
public interface IAccountRepository : IRepository<Account> { void DeleteInactiveAccountsOlderThanTwoYears(); void CreateAccountBasedOnExistingAccount(Account account); // Etc, etc, etc }
Then we have the concrete class that implements the repository specific interface (The NHibernateRepositoryBase class implements the members defined by IRepository
public class AccountRepository : NHibernateRepositoryBase<Account>, IAccountRepository { public void DeleteInactiveAccountsOlderThanTwoYears() {...}; public void CreateAccountBasedOnExistingAccount(Account account) {...}; // Etc, etc, etc }
So what I wanted is to scan our persistence assembly for any concrete types (IE: AccountRepository) that implement an interface (IE: IAccountRepository) that derives from a closed generic interface (IE: IRepository
I lifted the GenericConnectionScanner class (Which is what the ConnectImplementationsToTypesClosing convenience method uses) from the StructureMap code as my starting point and modified the logic:
public class DerivedOpenGenericInterfaceConnectionScanner : IRegistrationConvention { private readonly Type _openType; public DerivedOpenGenericInterfaceConnectionScanner(Type openType) { _openType = openType; if (!_openType.IsInterface || !_openType.IsOpenGeneric()) throw new ApplicationException( "This scanning convention can only be used with open generic interface types"); } public void Process(Type type, StructureMap.Configuration.DSL.Registry registry) { if (!type.IsConcrete()) return; var derivedTypes = type.GetInterfaces(). Where(i => i.GetInterfaces(). Any(i2 => i2.IsGenericType && i2.GetGenericTypeDefinition() == _openType)); if (derivedTypes.Count() > 0) registry.For(derivedTypes.First()).Add(type); } }
The constructor takes the open generic interface type. The Process method is called for each type; we only want concrete types. We check the type for interfaces that derive from an interface that has a type definition of our open generic interface type. If one exists we map the derived interface type to the concrete type.
ObjectFactory.Initialize( x => x.Scan( config => { config.AssemblyContainingType(typeof(IRepository<>)); config.With(new DerivedOpenGenericInterfaceConnectionScanner(typeof(IRepository<>))); })); var accountRepo = ObjectFactory.GetInstance<IAccountRepository>(); System.Diagnostics.Debug.Assert(accountRepo != null && accountRepo is AccountRepository);
The above initialization scans the assembly containing our open generic interface type and specifies our registration convention using the With() method. That's all there is to it!