Design-time (declarative) configuration vs run-time configuration

Nov 5, 2011 at 5:15 PM

I'm trying to avoid using attributes, but I have a need apply a dependency attribute to an injected constructor parameter.

I've achieved this through using a configuration file like so (using a StocksTicker lab example):

    <container>
      <register name="Console" type="ILogger" mapTo="ConsoleLogger"/>
      <register name="UI" type="ILogger" mapTo="TraceSourceLogger">
        <lifetime type="singleton"/>
        <constructor>
          <param name="traceSourceName" value="UI"/>
        </constructor>
      </register>      
      <register type="IStocksTickerView" mapTo="StocksTickerForm"/>
      <register type="IStockQuoteService" mapTo="RandomStockQuoteService">
        <constructor>
          <param name="logger">
            <dependency name="Console"/>
          </param>
        </constructor>
      </register>
      <register type="StocksTickerPresenter">
        <constructor>
          <param name="view" />
          <param name="stockQuoteService" />
          <param name="logger">
            <dependency name="UI"/>
          </param>
        </constructor>
      </register>
    </container>

You can see that there are two loggers registered using different names (ConsoleLogger and TraceSourceLogger registered with the names "Console" and "UI" respectively). The RandomStockQuoteService needs an instance of the logger registered as "Console" injected into the constructor. The StocksTickerPresenter needs an instance of the logger registered as "UI" injected into the constructor as well as an instance of IStockQuoteService, which in this case is the RandomStockQuoteService.

Now this works beautifully using the configuration file, but I haven't been able to figure out how to achieve the same named type mappings and dependencies at run-time using RegisterType/RegisterInstance.

Nov 5, 2011 at 8:10 PM
Edited Nov 6, 2011 at 4:04 AM

I think this should do what you want in C#:

IUnityContainer container = new UnityContainer();

container.RegisterType<ILogger, TraceSourceLogger>("Trace", 
    new ContainerControlledLifetimeManager(), 
    new InjectionConstructor("UI"));

container.RegisterType<ILogger, ConsoleLogger>("Console");

container.RegisterType<IStocksTickerView, StocksTickerForm>();

container.RegisterType<IStockQuoteService, RandomStockQuoteService>(
    new InjectionConstructor(
        container.Resolve<ILogger>("Console")
    )
);

container.RegisterType<StocksTickerPresenter>(
    new InjectionConstructor(
            container.Resolve<IStocksTickerView>(),
            container.Resolve<IStockQuoteService>(),
            container.Resolve<ILogger>("Trace")
    )
);

var presenter = container.Resolve<StocksTickerPresenter>();


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

Nov 5, 2011 at 9:28 PM
Edited Nov 5, 2011 at 10:03 PM

Thanks, Randy! I'll give it a try. BTW, what does the method call containter.RegisterType() (no arguments) do?

Edit: Nevermind about the last question. I see that your code is pseudo code since you omitted the type parameters from the method calls.

So, is this what's happening behind the scenes when the UnityContainer uses the configuration file to do type mapping? It seems like using the InjectionConstructor() along with Resolve() in the calls to RegisterType() is no longer leveraging Unity's ability to figure out how to inject the correct object instances into the right arguments of the constructor of types being resolved. This seems very manual to me.

Nov 6, 2011 at 4:05 AM
Edited Nov 6, 2011 at 4:05 AM

Sorry about the code formatting.  That was actual code that got mangled by the code formatter.  I've updated to include the generic types properly.

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

Nov 6, 2011 at 4:42 AM

You can get automatic constructor injection if you are using concrete types.  (I wasn't sure what the target classes looked like so I assumed interfaces were used.)

If the classes are defined with concrete types (e.g. StocksTickerPresenter takes a TraceSourceLogger) then Unity will perform automatic constructor injection:

IUnityContainer container = new UnityContainer();

container.RegisterType<TraceSourceLogger>(
    new ContainerControlledLifetimeManager(),
    new InjectionConstructor("UI"));

container.RegisterType<ConsoleLogger>();

container.RegisterType<IStocksTickerView, StocksTickerForm>();

container.RegisterType<IStockQuoteService, RandomStockQuoteService>();

container.RegisterType<StocksTickerPresenter>();
            
var presenter = container.Resolve<StocksTickerPresenter>();


I'm guessing that was more what you had in mind.

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

Nov 6, 2011 at 7:21 AM
Edited Nov 6, 2011 at 7:42 AM

No, I want to use interfaces, so your first version is more of what I wanted. Again, what I want to do was eliminate the need to use attributes like in this code:

    public class RandomStockQuoteService : IStockQuoteService
    {

        public RandomStockQuoteService([Dependency("Console")] ILogger logger)
        {
           ...
        }
        ...
    }

    public class StocksTickerPresenter
    {
        public StocksTickerPresenter(
            IStocksTickerView view,
            IStockQuoteService stockQuoteService, 
            [Dependency("UI")] ILogger logger)
        {
           ...
        }
        ...
    }

Having to use InjectionConstructor along with individual container.Resolve() calls seems like a lot of manual work for something that seems like it should be so fundamental--being able to group together type mappings. For instance, I'd like to be able to do something like this:

                container
                    .RegisterType<IWheel, CarWheel>("Car")
                    .RegisterType<IEngine, CarEngine>("Car")
                    .RegisterType<IVehicle, Car>("Car");
                container
                    .RegisterType<IWheel, MotorcycleWheel>("Motorcycle")
                    .RegisterType<IEngine, MotorcycleEngine>("Motorcycle")
                    .RegisterType<IVehicle, Motorcycle>("Motorcycle");

                var car = container.Resolve<IVehicle>("Car");
                var motorcycle = container.Resolve<IVehicle>("Motorcycle");

Both the Car and Motorcycle classes would take an IEngine and IWheel as constructor arguments.

 

Nov 7, 2011 at 7:46 AM
Edited Nov 7, 2011 at 9:59 AM

You could try something like this which gives you support for interfaces as well

[TestMethod]
public void UseDifferentLoggers()
{
  var container = new UnityContainer();
  container.RegisterType<ILogger, ConsoleLogger>();
  container.RegisterType<IStockQuoteService, RandomStockQuoteService>();
  container.RegisterType<StockTickerPresenter>(new InjectionConstructor(typeof(IStockQuoteService), typeof(UiLogger)));
  var service = container.Resolve<IStockQuoteService>();
  Assert.IsInstanceOfType(service.Logger, typeof(ConsoleLogger));
  var presenter = container.Resolve<StockTickerPresenter>();
  Assert.IsInstanceOfType(presenter.Logger, typeof(UiLogger));
  }
}
public interface ILogger {}
public class UiLogger : ILogger {}
public class ConsoleLogger : ILogger {}
public interface IStockQuoteService
{
  ILogger Logger { get; set; }
}
public class RandomStockQuoteService : IStockQuoteService
{
  public ILogger Logger { get; set; }
  public RandomStockQuoteService(ILogger logger)
  {
    Logger = logger;
  }
}
public class StockTickerPresenter
{
  public ILogger Logger { get; set; }
  public StockTickerPresenter(IStockQuoteService service, ILogger logger)
  {
    Logger = logger;
  }
}

This declares the ConsoleLogger as default mapping and tells Unity explicitely when to use the UiLogger instead.

Nov 7, 2011 at 9:16 PM
Edited Nov 7, 2011 at 9:16 PM

@Damien80, That's a little better since you need only pass types to InjectionConstructor() rather than having to call container.Resolve() explicitly. I just wish it would be easier to do something like my Car/Motorcycle example which doesn't require knowledge of the constructor arguments. I know that you can create a child container to get what I want, but that too is less than ideal.

Nov 8, 2011 at 8:54 AM
Edited Nov 8, 2011 at 9:06 AM

@child container: I would not bother using those. I have yet to find a scenario where you can do real dependency injection (not service location!) with child containers.

@something like my car/motorcycle example: Actually you can. It just takes "a little" tweaking of Unity. Don't nail me down to a clean implementation but this test passes for the happy case.

 

[TestMethod]
public void CanGroupRegistrations()
{
  var container = new UnityContainer();
  container.AddNewExtension<SemanticGroupExtension>();
  container.RegisterGroup<IVehicle, Car>("Car").Use<IWheel, CarWheel>().Use<IEngine, CarEngine>();
  container.RegisterGroup<IVehicle, Motorcycle>("Motorcycle").Use<IWheel, MotorcycleWheel>().Use<IEngine, MotorcycleEngine>();
  var car = container.Resolve<IVehicle>("Car");
  Assert.IsInstanceOfType(car.Wheel, typeof(CarWheel));
  Assert.IsInstanceOfType(car.Engine, typeof(CarEngine));
  var motorcycle = container.Resolve<IVehicle>("Motorcycle");
  Assert.IsInstanceOfType(motorcycle.Wheel, typeof(MotorcycleWheel));
  Assert.IsInstanceOfType(motorcycle.Engine, typeof(MotorcycleEngine));
}

 

And this is the quick and dirty code that works behind the scenes

 

public static class UnityContainerExtensions
{
  public static ISemanticGroup RegisterGroup<TFrom, TTo>(this IUnityContainer container, string name)
  {
    var extension = container.Configure<ISemanticGroupConfigurator>();
    return extension.RegisterGroup<TFrom, TTo>(name);
  }
}

public interface ISemanticGroupConfigurator : IUnityContainerExtensionConfigurator
{
  ISemanticGroup RegisterGroup<TFrom, TTo>(string name);
}

public interface ISemanticGroup
{
  ISemanticGroup Use<TFrom, TTo>();
}

public class SemanticGroupExtension : UnityContainerExtension, ISemanticGroupConfigurator
{
  protected override void Initialize()
  {
    var strategy = new SemanticGroupStrategy();
    Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
  }
  public ISemanticGroup RegisterGroup<TFrom, TTo>(string name)
  {
    return new SemanticGroup(new SemanticGroupContext(this), typeof(TFrom), typeof(TTo), name);
  }
  private class SemanticGroupContext : ISemanticGroupContext
  {
    private readonly SemanticGroupExtension semanticGroupExtension;
    public SemanticGroupContext(SemanticGroupExtension semanticGroupExtension)
    {
      this.semanticGroupExtension = semanticGroupExtension;
    }
    public IPolicyList Policies { get { return this.semanticGroupExtension.Context.Policies; } }
    public IUnityContainer Container { get { return this.semanticGroupExtension.Container; } }
  }
}

public interface ISemanticGroupContext
{
  IPolicyList Policies { get; }
  IUnityContainer Container { get; }
}

public class SemanticGroupStrategy : BuilderStrategy
{
  public override void PreBuildUp(IBuilderContext context)
  {
    IPolicyList resolverPolicyDestination;
    IConstructorSelectorPolicy selector = context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out resolverPolicyDestination);
    var ctor = selector.SelectConstructor(context, resolverPolicyDestination);
    if (ctor == null) { return; }
    ISemanticGroupPolicy policy = context.Policies.Get<ISemanticGroupPolicy>(context.BuildKey);
    if (policy == null) { return; }
    var parameters = ctor.Constructor.GetParameters();
    var parameterKeys = ctor.GetParameterKeys();
    for (int i = 0; i < parameters.Length; i++)
    {
      var @using = policy.Usings.FirstOrDefault(u => u.From == parameters[i].ParameterType);
      if (@using != null)
      {
        var resolverPolicy = new NamedTypeDependencyResolverPolicy(@using.From, @using.Name);
        context.Policies.Set<IDependencyResolverPolicy>(resolverPolicy, parameterKeys[i]);
      }
    }
    resolverPolicyDestination.Set<IConstructorSelectorPolicy>(
    new ReadonlyConstructorSelectorPolicy(ctor), context.BuildKey);
  }
}

public class ReadonlyConstructorSelectorPolicy : IConstructorSelectorPolicy
{
  private readonly SelectedConstructor selectedConstructor;
  public ReadonlyConstructorSelectorPolicy(SelectedConstructor selectedConstructor)
  {
    this.selectedConstructor = selectedConstructor;
  }
  public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination)
  {
    return selectedConstructor;
  }
}

public interface ISemanticGroupPolicy : IBuilderPolicy
{
  ICollection<Using> Usings { get; }
}

public class SemanticGroup : ISemanticGroup
{
  private readonly ISemanticGroupContext context;
  private readonly Type to;
  private readonly string name;
  private readonly Type @from;
  private SemanticGroupPolicy policy;
  public SemanticGroup(ISemanticGroupContext context, Type from, Type to, string name)
  {
    this.context = context;
    this.to = to;
    this.name = name;
    this.policy = new SemanticGroupPolicy();
    context.Policies.Set(typeof(ISemanticGroupPolicy), policy, new NamedTypeBuildKey(to, name));
    context.Container.RegisterType(from, to, name);
  }
  public ISemanticGroup Use<TFrom, TTo>()
  {
    policy.Usings.Add(new Using { From = typeof(TFrom), To = typeof(TTo), Name = this.name });
    context.Container.RegisterType(typeof(TFrom), typeof(TTo), this.name);
    return this;
  }
}

public class SemanticGroupPolicy : ISemanticGroupPolicy
{
  private readonly List<Using> usings;
  public SemanticGroupPolicy()
  {
    usings = new List<Using>();
  }
  public ICollection<Using> Usings { get { return usings; } }
}

public class Using
{
  public Type From { get; set; }
  public Type To { get; set; }
  public string Name { get; set; }
}

 

Using these test objects

 

public interface IWheel { }
public class CarWheel : IWheel { }
public class MotorcycleWheel : IWheel { }

public interface IEngine { }
public class MotorcycleEngine : IEngine { }
public class CarEngine : IEngine { }

public interface IVehicle
{
  IWheel Wheel { get; set; }
  IEngine Engine { get; set; }
}
public class Car : IVehicle
{
  public IWheel Wheel { get; set; }
  public IEngine Engine { get; set; }
  public Car(IWheel wheel, IEngine engine)
  {
    Wheel = wheel;
    Engine = engine;
  }
}
public class Motorcycle : IVehicle
{
  public IWheel Wheel { get; set; }
  public IEngine Engine { get; set; }
  public Motorcycle(IWheel wheel, IEngine engine)
  {
    Wheel = wheel;
    Engine = engine;
  }
}

 

Hope that helps

Nov 8, 2011 at 6:44 PM

Whoa! I wasn't expecting you to come up with a solution for me, although it's definitely appreciated. Thanks for the effort!

I thought of doing something similar to your example using child containers and a dictionary to keep track of the container instances corresponding to each group if Unity didn't support this concept of a "semantic group" out of the box. But since you say child containers aren't useful, I'll definitely check out your sample code. Thanks again!

 

Nov 9, 2011 at 5:50 AM

You are welcome :)