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<TEntity> : IQueryable<TEntity> where TEntity : class { TEntity Get(Guid id); void Save(TEntity entity); void Delete(TEntity entity); void DeleteSingle(IQueryable<TEntity> query); void DeleteMany(IQueryable<TEntity> 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<TEntity> not show below):
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<Account>) that is of a particular open generic interface type (IE: IRepository<>). Clear as mud?? :) Simply put, I want to scan for IRepository<> and want to map IAccountRepository to AccountRepository. This is cake with StructureMap using a registration convention. BTW, I’m using StructureMap 2.5.4 and from what I understand you may need this version to do what I’m doing here.
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!
Remember Me
a@href@title, b, i, strike, strong, u