Auto Mapping of Interfaces to Classes

May 2, 2014 at 9:13 PM
Hi,
I have an assembly with types ILogger, FileLogger and ConsoleLogger. When I use the automatic, conventional mappings, such as:

unity.RegisterTypes(AllClasses.FromAssemblies(new Assembly[] { Assembly.GetExecutingAssembly() }), WithMappings.FromMatchingInterface, WithName.Default, WithLifetime.ContainerControlled);

my types are not registered.
How can I achieve this in the most simple way?

Thanks!

RP
May 2, 2014 at 9:46 PM
OK, so, with:

unity.RegisterTypes(AllClasses.FromAssemblies(new Assembly[] { Assembly.GetExecutingAssembly() }), WithMappings.FromAllInterfacesInSameAssembly, WithName.Default, WithLifetime.ContainerControlled);

I get an error about duplicated mappings - ILogger already mapped to ConsoleLogger -, which makes kind of sense.
I tried a "clever" approach:

unity.RegisterTypes(AllClasses.FromAssemblies(new Assembly[] { Assembly.GetExecutingAssembly() }), WithMappings.FromMatchingInterface, x=>
{
unity.Registrations.Any(y => y.RegisteredType == x) ? WithName.TypeName(x) : WithName.Default(x)
}, WithLifetime.ContainerControlled);

But still no luck.

Any ideas?

RP
May 6, 2014 at 3:37 AM
I assume you want to register two ILoggers: one that maps to a FileLogger and one that maps to a ConsoleLogger. To do that you could use something like:
    IUnityContainer container = new UnityContainer();

    container.RegisterTypes(
        AllClasses.FromAssembliesInBasePath()
            .Where(t => typeof(ILogger).IsAssignableFrom(t)),
        WithMappings.FromAllInterfaces,
        WithName.TypeName,
        WithLifetime.ContainerControlled);

    var fileLogger = container.Resolve<ILogger>(typeof(FileLogger).Name);

This will register an ILogger mapped to the concrete type using the type name as part of the build key.

Also, note that because a lifetime manager is specified Unity will also register the concrete type along with the interface.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
May 6, 2014 at 5:58 AM
Hi, Randy!

Thanks for the answer. The problem is, I wanted to have a "default" (no name) registration.
I ended up with:
unity.RegisterTypes(AllClasses.FromAssemblies(new Assembly[] { Assembly.GetExecutingAssembly() }).Where(x => (x.GetInterfaces().Any() == true) && (x.IsAbstract == false) && (x.IsInterface == false)), WithMappings.FromAllInterfacesInSameAssembly, type => (unity.Registrations.Select(x => x.RegisteredType).Any(r => type.GetInterfaces().Contains(r) == true) == true) ? WithName.TypeName(type) : WithName.Default(type), WithLifetime.ContainerControlled);
This way, the first registration found is mapped without a name and all the others use their type name.
BTW, your comment:
Also, note that because a lifetime manager is specified Unity will also register the concrete type along with the interface.
Intrigued me... why is that?

Thanks,

RP
May 6, 2014 at 6:52 AM
I believe it is because of how Unity stores mapping separately from registrations. If injection members (or lifetime manager) are specified then the injection members need to be registered with the container along with the concrete type. If there are no injection members or lifetime manager then Unity knows how to create the default object.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
May 6, 2014 at 8:19 AM
Thanks Randy!

Do you know of any other way to achieve what I want - map first found interface without a name and all the others with one? Perhaps using a RegistrationConvention, I imagine.

RP
May 9, 2014 at 1:36 AM
You could create a RegistrationConvention but I wouldn't call that another way -- that would just be formalizing the registration logic in a Unity interface.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
May 9, 2014 at 1:17 PM
Randy,

Yes, of course, you are right. Thanks for the info.

RP