1

I am currently working on a IoC container abstraction that sits on top of StructureMap. The idea is that it could work with other IoC containers as well.

public interface IRegister
{
    IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments);
}

public abstract class ContainerBase : IRegister
{
    public abstract IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments);
}

public class StructureMapContainer : ContainerBase
{
    public StructureMapContainer(IContainer container)
    {
        Container = container;
    }

    public IContainer Container { get; private set; }

    public override IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments)
    {
        // StructureMap specific code
        Container.Configure(x =>
        {
            var instance = x.For(serviceType).Use(implementationType);
            arguments.ForEach(a => instance.CtorDependency<string>(a.Name).Is(a.Value));
        });

        return this;
    }
}

public class Argument
{       
    public Argument(string name, string value)
    {
        Name = name;
        Value = value;
    }

    public string Name { get; private set; }
    public string Value { get; private set; }
}

I can execute this code by doing the following:

//IContainer is a StructureMap type
IContainer container = new Container(); 
StructureMapContainer sm = new StructureMapContainer(container);

sm.RegisterType(typeof(IRepository), typeof(Repository));

The problem arises when I try to pass in constructor arguments. StructureMap allows you to fluently chain however many CtorDependency calls as you want but requires the constructor parameter name, value and type. So I could do something like this:

sm.RegisterType(typeof(IRepository), typeof(Repository), new Argument("connectionString", "myConnection"), new Argument("timeOut", "360"));

This code works as CtorDependency is currently of type string and both arguments are strings as well.

instance.CtorDependency<string>(a.Name).Is(a.Value)

How can I pass in multiple constructor arguments BUT of any type to this method? Is it possible? Any help would be much appreciated.

Thomas
  • 5,888
  • 7
  • 44
  • 83
  • Just a question: Why aren't you using something like `RegisterType(...)`? – Femaref Jun 07 '13 at 15:27
  • I do, I have extensions methods that have that signature but I decided to only include the necessary code to illustrate the problem at hand. – Thomas Jun 07 '13 at 15:38
  • Ah, right. Regarding your question: I think you won't get around using reflection here, but it should be appropiate to use it here. – Femaref Jun 07 '13 at 15:39
  • Could you give me an example so I can narrow down my search. Thanks. – Thomas Jun 07 '13 at 16:02
  • http://stackoverflow.com/a/4101821/127059 this might be a good starting point. Dynamic invocation of generic methods. – Femaref Jun 07 '13 at 16:24

1 Answers1

2

You could use a dynamic, anonymous object to pass in your arguments. Then use reflection to inspect the properties. The values can be any type.

dynamic example = new
{
    a = 3,
    b = "hello"
};

foreach (PropertyInfo p in example.GetType().GetProperties())
{
    string key = p.Name;
    dynamic value = p.GetValue(example, null);
    Console.WriteLine("{0}: {1}", key, value);
}

This outputs

a: 3
b: hello

Just modify as necessary for your needs.

Edit:

Just so this is clear. Your registration method would take a dynamic parameter. It would be something like this. I don't have StructureMap, but this should get you in the right direction.

public override IRegister RegisterType(Type serviceType, Type implementationType, dynamic arguments)
{
    // StructureMap specific code
    Container.Configure(x =>
    {
        var instance = x.For(serviceType).Use(implementationType);
        foreach (PropertyInfo p in arguments.GetType().GetProperties())
        {
            string key = p.Name;
            object value = p.GetValue(arguments, null);
            instance.CtorDependency<string>(key).Is(value));
        }
     });

    return this;
}

Edit Part Deux:

Okay, so you're wanting to be able to pass in any data type for the key. Is this closer to what you're needing? You get the method by name, the turn it into a closed generic by calling MakeGenericMethod. Then you invoke it.

var method = instance
    .GetType()
    .GetMethod("CtorDependency")
    .MakeGenericMethod(new Type[] { key.GetType() });
method.Invoke(instance, new object[] { key });
  • Amy, I think I did a poor job emphasizing where the core problem is. It is on this line: instance.CtorDependency(key).Is(value)) where T currently is string. This code is inside the foreach loop. The questions becomes what happens if the first argument is an int, the second a bool, and then a string... how do I translate that into instance.CtorDependency(key).Is(value)) then instance.CtorDependency(key).Is(value)) and finally instance.CtorDependency(key).Is(value))? – Thomas Jun 07 '13 at 16:00
  • Amy, I think you got it. I haven't tried the code yet but it sure looks like the right direction. Thank you! – Thomas Jun 07 '13 at 23:10