Fun with constructor arguments Part 2.1: Anonymous overrides revisited

After publishing the post on Anonymous overrides I thought about the topic again. In the end it didn’t make sense to put that functionality in a separate class.

The SmartConstructor already does most of the heavy lifting and matching the properties of an anonymous object to constructor parameters can easily be encapsulated in a new ParameterMatchingConvention.

As a result the SmartConstructor grew by a few lines of code (reading the properties of the anonymous object and converting them to ConstructorParameters) and I created a new convention called StringAsMappingNameMatchingConvention.

Get the overhauled source code for the SmartConstructor here (project TecX.Unity folder Injection and the test suite that shows how to use it in TecX.Unity.Test).

Advertisements

Fun with constructor arguments Part 3: Named registrations

A while ago there was a question on StackOverflow: If I have multiple implementations of an interface registered as named mappings and a constructor that should consume such a named mapping how can I tell Unity to automatically match the argument name to the mapping name?

E.g. if you have the following class definitions:

public interface ICommand { }

public class LoadCommand : ICommand { }

public class SaveCommand : ICommand { }

public class Consumer
{
  public Consumer(ICommand loadCommand, ICommand saveCommand)
  {
    // ...
  }
}

Now you setup your container and register both command implementations:

container.RegisterType<ICommand, LoadCommand>("loadCommand");
container.RegisterType<ICommand, SaveCommand>("saveCommand");

And when you resolve the Consumer the LoadCommand should be used for the parameter named loadCommand and the SaveCommand should be used for the parameter named saveCommand.

This is what you would have to do using Unity as is:

container.RegisterType<Consumer>(  new InjectionConstructor(
  new ResolvedParameter(typeof(ICommand), "loadCommand"),
  new ResolvedParameter(typeof(ICommand), "saveCommand")));

And this is a “slightly” enhanced version:

container.RegisterType<Consumer>(new MapParameterNamesToRegistrationNames());

The class MapParameterNamesToRegistrationNames is derived from InjectionMember. It places a marker policy in Unity’s build pipeline. When an object is resolved by the container a custom BuilderStrategy looks for that marker. If the marker is found the strategy will replace the dependency resolvers (implementations of IDependencyResolverPolicy) that Unity puts into the pipeline by default with NamedTypeDependencyResolvers using the name of the constructor argument as name of the mapping.

public class MapParameterNamesToRegistrationNamesStrategy : BuilderStrategy
{
  public override void PreBuildUp(IBuilderContext context)
  {
    if (context.Policies.Get(
      context.BuildKey) == null)
    {
      return;
    }
    IPolicyList resolverPolicyDestination;
    IConstructorSelectorPolicy selector =
      context.Policies.Get(
        context.BuildKey, out resolverPolicyDestination);
    if (selector == null)
    {
      return;
    }
    var selectedConstructor = selector.SelectConstructor(context, resolverPolicyDestination);
    if (selectedConstructor == null)
    {
      return;
    }
    ParameterInfo[] parameters = selectedConstructor.Constructor.GetParameters();
    string[] parameterKeys = selectedConstructor.GetParameterKeys();
    for (int i = 0; i < parameters.Length; i++)
    {
      Type parameterType = parameters[i].ParameterType;
      if (parameterType.IsAbstract ||
          parameterType.IsInterface ||
          (parameterType.IsClass && parameterType != typeof(string)))
      {
        IDependencyResolverPolicy resolverPolicy =
          new NamedTypeDependencyResolverPolicy(parameterType, parameters[i].Name);
        context.Policies.Set(resolverPolicy, parameterKeys[i]);
      }
    }
    resolverPolicyDestination.Set(
      new SelectedConstructorCache(selectedConstructor), context.BuildKey);
  }
}

The registration code tells you exactly what you expect from the container. No confusing setup of InjectionConstructors and ResolvedParameters. Just another simple convention that can make your life a lot easier.

Get the source code for MapParameterNamesToRegistrationNames here (project TecX.Unity folder Injection and the test suite that shows how to use it in TecX.Unity.Test).

Fun with constructor arguments Part 2: Anonymous overrides

An interesting feature of Castle Windsor is the ability to specify parameter overrides for a constructor using anonymous objects.

You define an anonymous object with properties named like the parameter you want to override and assign some value to it. You can either directly provide a value for that parameter or the name of a mapping you registered for the type of that parameter.

That means if you have a class with a dependency like this one:

public class MyService : IMyService
{
  public IFoo SomeFoo { get; set; }
  public MyService(IFoo someFoo)
  {
    this.SomeFoo = someFoo;
  }
}

and two classes that implement IFoo called Foo and Bar. Foo is registered as the default mapping (without a name) and Bar is registered as an additional mapping for IFoo with the name “Bar”:

container.RegisterType<IFoo, Foo>();
container.RegisterType<IFoo, Bar>("Bar");

You can use the AnonymousParameterOverride to specify that you want to use the named mapping instead of the default one:

container.RegisterType<IMyService, MyService>(new AnonymousParameterOverride(new  { foo = "Bar" });

Now this looks a little bit better than the default way Unity offers you to do this which is something like this:

container.RegisterType<IMyService, MyService>(
  new InjectionConstructor(new ResolvedParameter(typeof(IFoo), "Bar"));

But still not quite as fluent as I would like to see it. Unity does not really have a fluent configuration API but I took the liberty to write one.
The ConfigurationBuilder will be the subject of a series of posts on its own so for now let me just show you a glimpse at what is possible with it:

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.For<IMyService>().Use<MyService>().Ctor(new { foo = "Bar" });

Much better isn’t it?

Get the source code for the AnonymousParameterOverride here (project TecX.Unity folder Injection and the test suite that shows how to use it in TecX.Unity.Test).

Fun with constructor arguments Part 1: Pick & Choose

One of Unity’s weaknesses is the verbosity of its configuration. While other containers support developers with various built-in conventions to keep the necessary setup code as compact as possible Unity requires you to state everything you want explicitely.

Consider the following skeleton of a class definition:

public class CustomerRepository
{
  public CustomerRepository(string connectionString, ILog log, ISomethingElse else) { ... }
}

Specifying the connectionString parameter for that constructor using Unity’s standard API looks like this:

container.RegisterType<ICustomerRepository, CustomerRepository>(
  new InjectionConstructor("I'mAConnectionString", typeof(ILog), typeof(ISomethingElse)));

There are a couple of things I don’t like about this approach:

  • Why do I have to write so much code to specify a single parameter?
  • Why do I have to specify all parameters although I care about only one?
  • Why does their order matter? Refactoring could break my registration code!
  • Why do I have to find out on my own that I can provide placeholders for parameters I don’t care about by providing their type?
  • Why do I have to provide those placeholders at all?

It’s all about verbosity. I don’t like to write unnecessary code. That is code I will have to write, test and maintain. The more effort I can save on that the better.

Conventions are a great means to not have to write code. They will get you 80% of the way most of the time at virtually no cost. And for the last 20% you can use the verbose API or define custom conventions that fit the special needs of your environment.

Providing a single parameter for the constructor of CustomerRepository can be as simple as this:

container.RegisterType<ICustomerRepository, CustomerRepository>(
  new SmartConstructor("I'mAConnectionString"));

What do you have to do to get that convenience? Not that much actually. SmartConstructor uses a couple of conventions to select a constructor from a set of candidates:

  • Only consider constructors that accept all provided parameters
  • Don’t consider constructors that have primitive parameters (like strings or integers) that are not satisfied by the provided parameters
  • If the parameter you specified is a string try to match it with parameters whose names contain connectionString, file or path.
  • Try to match specified parameters by parts of their type name. E.g. if you specified a parameter of type SomeTypeName a convention will look for parameters named someTypeName, typeName and name.
  • From the candidates that are left take the one with the most parameters (most greedy constructor).

The matching conventions are easy to write. They derive from ParameterMatchingConvention

public abstract class ParameterMatchingConvention
{
  public bool Matches(ConstructorParameter argument, ParameterInfo parameter)
  {
    ResolvedParameter rp = argument.Value as ResolvedParameter;
    Type argumentValueType = rp != null ? rp.ParameterType : argument.Value.GetType();
    if (argument.Value != null &&
        parameter.ParameterType.IsAssignableFrom(argumentValueType))
    {
      return this.MatchesCore(argument, parameter);
    }
    return false;
  }
  protected abstract bool MatchesCore(ConstructorParameter argument, ParameterInfo parameter);
}

That base class does some validation of the input values (which is omitted for brevity in the sample) and checks wether the type of the specified parameter matches the type of the parameter it is compared against. If that’s the case it hands over to the actual implementation of the convention. The ConnectionStringMatchingConvention for example looks as simple as that:

public class ConnectionStringMatchingConvention : ParameterMatchingConvention
{
  protected override bool MatchesCore(ConstructorParameter argument, ParameterInfo parameter)
  {
    if (parameter.ParameterType == typeof(string))
    {
      return parameter.Name.Contains("connectionString", StringComparison.OrdinalIgnoreCase);
    }
    return false;
  }
}

Done. To add a custom convention to the selection process you can call an extension method of IUnityContainer:

container.WithConstructorArgumentMatchingConvention(new MyCustomConvention());

Get the source code for the SmartConstructor here (project TecX.Unity folder Injection and the test suite that shows how to use it in TecX.Unity.Test).