Automatically Register Implementers of a Generic Interface With Unity
It's quite a common pattern to have a generic interface that is implemented by a number of concrete types and to want to be able to instantiate the concrete implementation by the type of generic parameter.
For example, given an interface IPat<T>, concrete classes Cat and Dog, we might have CatPatter implementing IPat<Cat> and DogPatter implementing IPat<Dog>. With no IoC container, to pat our pet based on the type of pet, our code needs the explicit knowledge that the CatPatter and DogPatter classes implement IPat<T>.
With an IoC container, we can register the types so all we need to do is ask the container for an instance of IPat<T> for out type of pet, but (at least with Unity) we still need to manually register each implementation of the IPat<> interface. This is clearly not ideal!
With a bit of reflection funkery, we can automatically register types that implement a generic interface.
1 public static void RegisterAllTypesForOpenGeneric(this IUnityContainer container, Type openGenericType, Assembly targetAssembly)
2 {
3 if (!openGenericType.IsGenericTypeDefinition)
4 throw new ArgumentException("typeToRegister must be an open generic type", "typeToRegister");
5
6 foreach (Type type in targetAssembly.GetExportedTypes())
7 {
8 if (openGenericType.IsInterface)
9 RegisterInterfaceTypes(container, openGenericType, type, type);
10 else
11 RegisterBaseTypes(container, openGenericType, type, type);
12 }
13 }
14
15 private static void RegisterInterfaceTypes(IUnityContainer container, Type openGenericType, Type targetType, Type typeToRegister)
16 {
17 foreach (Type interfaceType in targetType.GetInterfaces())
18 if (interfaceType.IsGenericType && !interfaceType.ContainsGenericParameters && openGenericType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition()))
19 container.RegisterType(interfaceType, typeToRegister);
20 }
21
22 private static void RegisterBaseTypes(IUnityContainer container, Type openGenericType, Type targetType, Type typeToRegister)
23 {
24 if (targetType.BaseType != null && targetType.BaseType != typeof(object))
25 if (targetType.BaseType.IsGenericType && openGenericType.IsAssignableFrom(targetType.BaseType.GetGenericTypeDefinition()))
26 container.RegisterType(targetType.BaseType, typeToRegister);
27 else
28 RegisterBaseTypes(container, openGenericType, targetType.BaseType, typeToRegister);
29 }
1 [TestClass]
2 public class AutoRegistrationTest
3 {
4 [TestMethod]
5 public void WhenPassedOpenGenericInterfaceType_AllDerivedTypesAreRegistered()
6 {
7 // use a strict mock to ensure no other types are registered
8 var container = MockRepository.GenerateStrictMock<UnityContainerBase>();
9 container.Expect(x => x.RegisterType(typeof(ITest<A>), typeof(TestA))).Return(container);
10 container.Expect(x => x.RegisterType(typeof(ITest<B>), typeof(TestB))).Return(container);
11 container.RegisterAllTypesForOpenGeneric(typeof(ITest<>));
12 }
13
14 [TestMethod]
15 public void WhenPassedOpenGenericConcreteType_AllDerivedTypesAreRegistered()
16 {
17 // use a strict mock to ensure no other types are registered
18 var container = MockRepository.GenerateStrictMock<UnityContainerBase>();
19 container.Expect(x => x.RegisterType(typeof(TestBase<A>), typeof(TestA))).Return(container);
20 container.Expect(x => x.RegisterType(typeof(TestBase<B>), typeof(TestB))).Return(container);
21 container.RegisterAllTypesForOpenGeneric(typeof(TestBase<>));
22 }
23
24 [TestMethod]
25 public void WhenBaseClassImplementsGenericType_DerivedTypesAreRegistered()
26 {
27 // use a strict mock to ensure no other types are registered
28 var container = MockRepository.GenerateStrictMock<UnityContainerBase>();
29 container.Expect(x => x.RegisterType(typeof(ITestBase<A>), typeof(TestBaseWithInterfaceA))).Return(container);
30 container.Expect(x => x.RegisterType(typeof(ITestBase<B>), typeof(TestBaseWithInterfaceB))).Return(container);
31 container.RegisterAllTypesForOpenGeneric(typeof(ITestBase<>));
32 }
33 }
34
35 public interface ITest<T> { }
36 public class TestBase<T> { }
37
38 public class TestA : TestBase<A>, ITest<A> { }
39 public class TestB : TestBase<B>, ITest<B> { }
40
41 public class A { }
42 public class B { }
43
44 public class TestBaseWithInterface<T> : ITestBase<T> { }
45 public interface ITestBase<T> { }
46 public class TestBaseWithInterfaceA : TestBaseWithInterface<A> { }
47 public class TestBaseWithInterfaceB : TestBaseWithInterface<B> { }
Sorry about the formatting. Ergh. UPDATE: Formatting is still not ideal, I used CopySourceAsHTML plugin for VS2008. I can't install anything here at work, so no Live Writer :-\
3 Comments:
Okay I'm so not reading that code.
I suggest using live writer and "vs paste". Hanselman did a a decent post on some of the options recently:
http://www.hanselman.com/blog/HowToPostCodeToYourBlogAndOtherReligiousArguments.aspx
It's a little bit better. Still fugly. The output of the CopySourceAsHTML plugin doesn't seem to play well with the theme I have. Damn line spacing. I should probably do some work rather than frigging with this.
Thanks for the link anyways! I'll be checking this stuff out from home for sure.
Thank you so much. It saved lot of my time!!!!
Post a Comment
Note: Only a member of this blog may post a comment.
<< Home