Tuesday, July 19, 2016

Registering All Implementations of an Interface with Unity

Currently, the extensibility story for the TrafficCamBot requires anyone wanting to add a new set of cameras to implement a specific .NET interface (directly inside the bot assembly, although this may change in the future). All implementations of said interface need to be registered with the .NET Unity dependency injection container, for this is how the master "camera data manager" finds them.

Originally, I was using an overload of UnityContainer.RegisterType that took generic type parameters. This is well-and-good, but because generic type parameters are really only helpful at compile-time, this approach would have required each new implementation to add an individual line of additional code to call RegisterType.

            container.RegisterType<ICameraDataService, SeattleCameraDataService>(
                typeof(SeattleCameraDataService).Name, new ContainerControlledLifetimeManager());
            container.RegisterType<ICameraDataService, BayAreaCameraDataService>(
                typeof(BayAreaCameraDataService).Name, new ContainerControlledLifetimeManager());
            // etc.

Ideally, this would not be necessary, as it is an additional step for the camera data contributor and is prone to error. Fortunately, we can reflect over all of the types in the assembly looking for appropriate implementations and use a non-generic RegisterType overload as follows:

            var cameraDataServiceTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                                         where t.IsClass && !t.IsAbstract && t.GetInterface(typeof(ICameraDataService).Name) != null
                                         select t;
            foreach (var cameraDataServiceType in cameraDataServiceTypes)
                container.RegisterType(typeof(ICameraDataService), cameraDataServiceType,
                    cameraDataServiceType.Name, new ContainerControlledLifetimeManager(),
                    new InjectionMember[] { });

Later, since Unity is happy with multiple registered implementations per interface, these can be snarfed out of the container using UnityContainer.ResolveAll:

            var managers = UnityConfig.GetConfiguredContainer().ResolveAll(typeof(ICameraDataService));
            foreach (ICameraDataService manager in managers) {
                var service = manager as ICameraDataService;
                services[service.Name] = service;

No comments: