resolve multiple implementations for one interface

May 10, 2012 at 2:03 AM

Hi,

I'm sorry if this is answered somewhere else but having a hard time finding...

I am currently a Spring.NET user and thinking of switching over to unity.

Spring allows me to resolve multiple dependancies for a single interface in one container. I'm assuming unity will allow me to do the same thing. I just would like to know how through configuration.

Simply, if ILogger is my interface and i would like to resolve TraceLogger for one implementation and NullLogger for another implementation and have that use one configuration file, what does that look like?

 

thanks.

 

May 10, 2012 at 4:45 AM
Edited May 10, 2012 at 5:54 AM

Yes, you can register interface/type mappings and assign them a name.  Let's use your example of an ILogger interface with TraceLogger and NullLogger implementations.

We can use the configuration to register the types in the container:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
              Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">        
    <container>
      <register type ="UnityDemo.ILogger, UnityDemo" mapTo="UnityDemo.TraceLogger, UnityDemo" name="TraceLogger" />
      <register type ="UnityDemo.ILogger, UnityDemo" mapTo="UnityDemo.NullLogger, UnityDemo" name="NullLogger" />
    </container>
  </unity>  
</configuration>

I've set the names to be TraceLogger and NullLogger but the names are arbitrary and need not be related to the actual type.  Now we can resolve the ILogger interface using the name registered in the configuration file:

IUnityContainer container = new UnityContainer();
container.LoadConfiguration();

var logger1 = container.Resolve<ILogger>("NullLogger");
var logger2 = container.Resolve<ILogger>("TraceLogger");

// Get all ILoggers's that are registered
IEnumerable<ILogger> loggers = container.ResolveAll<ILogger>();

OK, but you may not want to pepper your code with ILogger names.  It would be nicer if Unity would handle that for us.  

Next let's consider a class called DisplayManager:

    public class DisplayManager
    {
        ILogger logger;

public DisplayManager(ILogger logger) { this.logger = logger; } }

We can resolve DisplayManager from the UnityContainer (no registration required):

DisplayManager manager = container.Resolve<DisplayManager>();

But when we do that we are greeted by the following exception:

InvalidOperationException - The current type, UnityDemo.ILogger, is an interface and cannot be constructed. Are you missing a type mapping?

What's going on here?  Well, the problem is that the DisplayManager class constructor accepts an ILogger but we have registered 2 ILogger types with the container so Unity does not know which ILogger to inject.  The solution is to configure our DisplayManager type and indicate what type of ILogger the DisplayManager constructor should receive:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
              Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">        
    <container>
      <register type ="UnityDemo.ILogger, UnityDemo" mapTo="UnityDemo.TraceLogger, UnityDemo" name="TraceLogger" />
      <register type ="UnityDemo.ILogger, UnityDemo" mapTo="UnityDemo.NullLogger, UnityDemo" name="NullLogger" />
      <register type="ConsumeUnity.DisplayManager, ConsumeUnity">
        <constructor>
          <param name="logger" dependencyName="TraceLogger" />
        </constructor>
      </register>
    </container>
  </unity>  
</configuration>

Now when we request a DisplayManager from the Unity container it will be injected with a TraceLogger.

I hope that helps.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

May 10, 2012 at 11:31 PM

Randy,

 

your display manager scenario is EXACTLY what I was looking for... i have been a long time Spring.NET user and still love what it can do, but i think with the interceptors capabilities that Unity has to offer and the ability to differentiate dependencies between implementations allows me to consider Unity for my new company that seems to really stick with Microsoft technologies...

Thanks for the help.

P.S. - Unity doesn't have an expression language like Spring.NET does it?

May 11, 2012 at 5:25 AM

> P.S. - Unity doesn't have an expression language like Spring.NET does it?

No, it doesn't.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

May 24, 2012 at 5:41 PM

Hello,

I am not creating new thread cause i think my question is in some manner related to this topic.
So i would like to ask if we can disable Unity autowiring feature (like the spring framework in its default configuration) ? ( afraid that the project becoming huge with a number of developers & developer teams and some could foget dependencies without being warned)

thx

May 29, 2012 at 3:36 AM

There is no easy way to disable autowiring (such as configuration).  In principle, you might be able to create a container extension to clear policies, strategies and replace them to obtain the desired behavior.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jul 5, 2012 at 11:35 PM
Edited Jul 5, 2012 at 11:39 PM

Hello Randy

I would like to do the same configuration wireup above programatically. How can I achieve that?

My project is as follows. I am writing a three-tier application. One tier is the Data Access Layer.

public abstract class DAO
{
    private IDbConnection connection;

    public DAO(IDbConnection connection)
    {
        this.connection = connection;
    }

    // lots of code
}

public class DAOAutomation : DAO
{
    public DAOAutomation(IDbConnection connection)
        : base(connection)
    {

    }
}

public class DAOExternal : DAO
{
    public DAOExternal(IDbConnection connection)
        : base(connection)
    {

    }
}

I would like to inject a fresh new OleDbConnection into every DAOAutomation instance, and a fresh new OracleConnection into every DAOExternal. That means my Unity container would look like this programatically:

public class EntryPoint
{
    public class EntryPoint()
    {
        IUnityContainer container = new UnityContainer();
        container
            .RegisterType<IDbConnection, OleDbConnection>("access")
            .RegisterType<IDbConnection, OracleConnection>("oracle");
    }
}

How do I go on and wire up each different IDbConnection to DAOAutomation and to DAOExternal? I thought something on the lines of

            container
                .RegisterType<DAOAutomation>(InjectParameter.ToParameter<IDbConnection>("access"))
                .RegisterType<DAOExternal>(InjectParameter.ToParameter<IDbConnection>("oracle")); 

but it didn't work out. Please help me :)

Jul 6, 2012 at 12:27 AM

Since you want a fresh connection for every resolve you can use an InjectionConstructor combined with a ResolvedParameter.  That way the connection will be resolved from the container every time the DAO object is resolved/injected which will give you a new instance (by default).  For example:

    IUnityContainer container = new UnityContainer();

    container
        .RegisterType<IDbConnection, OleDbConnection>("access", 
            new InjectionConstructor(
                @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\mydatabase.mdb;User Id=admin;Password=;"))
        .RegisterType<IDbConnection, OracleConnection>("oracle",
            new InjectionConstructor(
                @"Data Source=MyOracleDB;Integrated Security=yes;"));

    container
        .RegisterType<DAOAutomation>(
            new InjectionConstructor(
                new ResolvedParameter<IDbConnection>("access")));

    container
        .RegisterType<DAOExternal>(
            new InjectionConstructor(
                new ResolvedParameter<IDbConnection>("oracle")));

            
    var automationDAO = container.Resolve<DAOAutomation>();
    var externalDAO = container.Resolve<DAOExternal>();

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com