Accessing Injection Members

Jul 8, 2015 at 2:50 PM
Hello there,

we have a unit config xml file for configuring our unity container. At some point in the application, we change the registered types with an other implementation of the interface.
The problem is, that the Injection Members are overwritten by the call
container.RegisterType<Interface, Implementation>(). I know that I can pass in the Injection Members in that function. But how can I get the Injection Members of an existing registration and re-use it for the new registration?
Jul 8, 2015 at 5:57 PM
Unity maintains the interface to concrete mapping and the injection members separately. The injection members are associated with the concrete registered type and not the type mapping. So when you register a mapping that points to a new concrete type it will have no injection members already defined. Also, the injection members will contain specific (reflection) information about the concrete type which is not applicable to a new registration. i.e. you can't just point the existing policies to the new concrete type.

So, yes, you can get the existing injection members but (mostly) they cannot be reused for the new concrete type.
Jul 9, 2015 at 6:00 AM
Thank you randylevy :).
Okay, I can't reuse them, but I think I can build some new injection members with the informations? (checking which exists and then create new injection members).
But I haven't found a way to retrieve the existing injection members in the current api. Maybe you can give me an example?
Jul 9, 2015 at 6:56 AM
You can retrieve the policy. However, there are no useful methods or properties that would let you extract the information programmatically. The information is there however it's contained in private member variables. You could access them using reflection but that is usually not a good idea. Plus even if you could you would need to translate (using code) all the relevant data to new InjectionMembers. Similarly if you try to parse the configuration and try to translate the config to code it's going to be painful.

The easiest way would be read in the configuration and then make the appropriate changes to the config and then use that configuration section in the call to LoadConfiguration.

Here's an example. First the config:
<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type=" Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <namespace name="ConsoleApplication" />
    <assembly name="ConsoleApplication" />
    <container>
      <register type="ILogger" mapTo="Logger">
        <constructor />
      </register>
    </container>
  </unity>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

An ILogger is registered and mapped to Logger and the constructor is set as the default constructor. This is because we want to use the default constructor and not the constructor that takes an ISettings (to avoid a resolve error because we didn't register an ISettings). Unity would normally choose the ISettings constructor since it has the most parameters.

And the code:
    public interface ILogger { }
    
    public class Logger : ILogger
    {
        public Logger()
        {
        }

        public Logger(ISettings settings)
        {
        }
    }

    public class Logger2 : ILogger
    {
        public Logger2()
        {
        }

        public Logger2(ISettings settings)
        {
        }
    }

Now at startup we change the registration of ILogger to Logger2:
    IUnityContainer container = new UnityContainer();

    // Get the config
    var config = (Microsoft.Practices.Unity.Configuration.UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    
    // Get the ILogger registration
    var registration = config.Containers.First()
                        .Registrations.FirstOrDefault(r => 
                            r.TypeName == typeof(ILogger).Name);

    if (registration != null)
    {
        // set the private _bReadOnly field to false to avoid "Configuration is read only" exception
        var readOnlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
        readOnlyField.SetValue(registration, false);
        
        // Change the mapping from Logger to Logger2
        registration.MapToName = typeof(Logger2).Name;
    }

    // Load the new config
    container.LoadConfiguration(config);
    
    // Resolve ILogger which will now return Logger2 and use the default constructor
    var log = container.Resolve<ILogger>();