Unity 3: auto registering multiple implementations for one interface

Oct 27, 2013 at 1:24 PM
Edited Oct 27, 2013 at 2:18 PM
I have an interface IFoo<T>

Multiple concrete classes implement IFoo<T> as follows:

MyConcreteClass : IFoo<MyConcreteClass>
AnotherConcreteClass : IFoo<AnotherConcreteClass>

As you can see registering by interface name convention is not an option here. Does Unity-3 support an auto-registration by convention scheme, or other, which will enable me to use RegisterTypes when WithMappings.FromInterfaceName is not an option? Conceptually I'm looking for something that approximates "WithMappings.FromImplementation".

Thank you in advance.

-N.
Oct 27, 2013 at 4:26 PM
I have a possible solution. It seems to be working as I want but I'm not certain it's bullet proof.
For example, it is possible, though unlikely, the ExportedTypes could be null.
So suggestions to improve on this are appreciated.
            var typs = AppDomain
                .CurrentDomain
                .GetAssemblies()
                .Where(t => t.FullName.StartsWith("Foo"))
                .Select(t=> t.ExportedTypes);

            container.RegisterTypes(
                typs.First()
                , t =>  t.GetInterfaces()
                , WithName.Default);
-N
Oct 28, 2013 at 6:27 AM
Edited Oct 28, 2013 at 11:36 PM
When I try to run the posted code, I get a DuplicateTypeMappingException:
public interface IFoo { }

public class MyConcreteClass : IFoo { }

public class AnotherConcreteClass : IFoo { }

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        var typs = AppDomain
            .CurrentDomain
            .GetAssemblies()
            .Where(a => a.FullName.StartsWith("Foo"))
            .Select(t => t.ExportedTypes);

        container.RegisterTypes(
            typs.First()
            , t => t.GetInterfaces()
            , WithName.Default);
    }
}

What about registering IFoo to the concrete class by type name?
container.RegisterTypes(
    AllClasses.FromLoadedAssemblies()
    .Where(t => t.Namespace == "UnityAppNS"),
    WithMappings.FromAllInterfacesInSameAssembly,
    WithName.TypeName);

IFoo foo1 = container.Resolve<IFoo>(typeof(MyConcreteClass).Name);
IFoo foo2 = container.Resolve<IFoo>(typeof(AnotherConcreteClass).Name);

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Oct 28, 2013 at 1:16 PM
Randy;

I think the reason you get the DuplicateTypeMappingException is because your definition of IFoo is not of type <T>.
I have IFoo<T> and concrete classes implement as ConcreteClass : IFoo<ConcreteClass>.
Oct 28, 2013 at 11:35 PM
Edited Oct 28, 2013 at 11:41 PM
Sorry about that...don't know what I was thinking. Ignore the exception part but I think in general the approach is sound:
public interface IFoo<T> { }

public class MyConcreteClass : IFoo<MyConcreteClass> { }

public class AnotherConcreteClass : IFoo<AnotherConcreteClass> { }

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies()
            .Where(t => t.Namespace == "UnityAppNS"),
            WithMappings.FromAllInterfacesInSameAssembly,
            WithName.TypeName);

        var foo1 = container.Resolve<IFoo<MyConcreteClass>>(typeof(MyConcreteClass).Name);
        var foo2 = container.Resolve<IFoo<AnotherConcreteClass>>(typeof(AnotherConcreteClass).Name);
    }
}

Or default registrations since IFoo<MyConcreteClass> is a different type than IFoo<AnotherConcreteClass>:
IUnityContainer container = new UnityContainer();

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies()
    .Where(t => t.Namespace == "UnityByConvention_463772"),
    WithMappings.FromAllInterfacesInSameAssembly,
    WithName.Default);

var foo1 = container.Resolve<IFoo<MyConcreteClass>>();
var foo2 = container.Resolve<IFoo<AnotherConcreteClass>>();

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Marked as answer by Nobadeer on 10/29/2013 at 4:14 AM
Oct 29, 2013 at 11:14 AM
Thanks Randy. I appreciate your help.
Jan 24, 2014 at 12:58 PM
Edited Jan 24, 2014 at 2:09 PM
I have followed the above but what is different with my implementation is that I have an interface which a few classes need to implement:
namespace.area1 {
    public class ModuleMenu : INavigationProvider
    {
        public ModuleMenu()
        {
        }
    }
}

namespace.area2 {
    public class ModuleMenu : INavigationProvider
    {
        public ModuleMenu()
        {
        }
    }
}
Now obviously I do get the DuplicateTypeMappingException. This is, I suppose, expected because I will the be implemented more than once, but I still need to get and resolve all instance in order to inject them as a constructor paramenter in another class?

The other class I need this for has looks something like this:
public class NavigationManager : INavigationManager
    {
        IEnumerable<INavigationProvider> _navigationProviders;

        public NavigationManager(IEnumerable<INavigationProvider> navigationProviders)
        {
            _navigationProviders = navigationProviders;
        }
    }
var moduleMenus = container.ResolveAll<INavigationProvider>();
container.RegisterInstance<INavigationManager>(new NavigationManager(moduleMenus));
So, as can be seen, I need to resolve all the implementations of INavigationProvider in order for it to be injected into the NavigationManager.

Any ideas how I can achieve this?
Jan 27, 2014 at 6:50 AM
You are getting a DuplicateTypeMappingException because the class names are the same.

Here is one way to register just the INavigationProvider's:
IUnityContainer container = new UnityContainer();

container.RegisterTypes(
    AllClasses.FromAssembliesInBasePath()
    .Where(t => typeof(INavigationProvider).IsAssignableFrom(t)),
    WithMappings.FromAllInterfaces,
    t => t.FullName);

container.RegisterType<IEnumerable<INavigationProvider>>(new InjectionFactory(c => c.ResolveAll<INavigationProvider>()));
container.RegisterType<INavigationManager, NavigationManager>(new ContainerControlledLifetimeManager());

var navigationManager = container.Resolve<INavigationManager>();

Some notes about the above code:
  • I use FromAssembliesInBasePath since I assume you will have a soft dependency on the Modules
  • Only types that implement INavigationProvider are retrieved
  • Since in the posted sample code each INavigationProvider class (e.g. Area1) only implements one interface WithMappings.FromAllInterfaces is used
  • The full class name is used to avoid a DuplicateTypeMappingException
  • IEnumerable<INavigationProvider> is registered to return all named INavigationProvider's. Anytime IEnumerable<INavigationProvider> is required they will automatically be injected
  • The NavigationManager is register as a singleton
~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jan 27, 2014 at 7:46 AM
Awesomeness! Thanks