0

I want to implement the decorator pattern using StructureMap 4.
I've created an interface IDeveloper and two implementations
CSharpDeveloper which is the decorated type, and DeveloperDecorator
which is the decorator.
The decorator type has a dependency on IDeveloper and the class itself implements IDevloper, which means that one of the types should be a named instance.
The named instance is CSharpDeveloper

public interface IDeveloper
{
}

public class CSharpDeveloper : IDeveloper
{
}

public class DeveloperDecorator : IDeveloper
{
    private readonly IDeveloper developer = null;

    public DeveloperDecorator(IDeveloper developer) {
        this.developer = developer;
    }
}

I want to register the DeveloperDecorator as an IDeveloper that I could resolve the type using unnamed GetInstance overload function

container.GetInstance<IDeveloper>();

and StructureMap will be able to resolve the dependent type as the named instance. Something like this (concept code)

Func<IDeveloper> factory = () => {
    return new DeveloperDecorator(container.GetInstance<IDeveloper>("name"));
};               

I can't use generics at all so I can not use the Ctor<TCtorType>() api.
I have a type called TypeMap which contains the service type, concrete type and optional name.

public class TypeMap
{
    public string Name { get; set; }
    public Type ServiceType { get; set; }
    public Type ConcreteType { get; set; }
}

and I've tried to use Dependencies but nothing worked.

public void Register(TypeMap typeMap, ITypeMapCollection dependencies = null) {
    container.Configure(x => {
        var use = x.For(typeMap.ServiceType)
                   .Add(typeMap.ConcreteType);

        if (typeMap.Name.IsNotNullOrEmpty()) {
            use.Named(typeMap.Name);
        }

        if (dependencies.IsNotNullOrEmpty()) {
            dependencies.ForEach(dependency => {
                use.Dependencies.Add(dependency.Name, dependency.ServiceType);
            });
        }
    });
}

Thanks.

Sagi
  • 8,972
  • 3
  • 33
  • 41

1 Answers1

0

I've managed to do so by supplying the Use method an expression tree that creates the instance and resolve all dependencies by the container.

public void Register(TypeMap typeMap, ITypeMapCollection dependencies = null) {
    container.Configure(x => {
        var use = x.For(typeMap.ServiceType)
                   .Use(typeMap.ConcreteType);

        if (typeMap.Name.IsNotNullOrEmpty()) {
            use.Named(typeMap.Name);
        }

        if (dependencies.IsNotNullOrEmpty()) {
            x.For(typeMap.ServiceType).Use("composite", BuildExpression(typeMap, dependencies));
        }
    });
}

private Func<IContext, object> BuildExpression(TypeMap typeMap, ITypeMapCollection dependencies) {
    var contextParameter = Expression.Parameter(typeof(IContext), "context");
    var @params = dependencies.ToArray(d => d.ServiceType);
    var ctorInfo = typeMap.ConcreteType.GetConstructor(@params);
    var genericMethodInfo = typeof(IContext).GetMethods().First(method => {
        return method.Name.Equals("GetInstance") &&
                method.IsGenericMethodDefinition &&
                method.GetParameters().Length == 1;
    });

    var getInstanceCallExpressions = dependencies.Select(dependency => {
        var nameParam = Expression.Constant(dependency.Name, typeof(string));
        var methodInfo = genericMethodInfo.MakeGenericMethod(new[] { dependency.ServiceType });

        return Expression.Call(contextParameter, methodInfo, new[] { nameParam });
    });

    var lambda = Expression.Lambda<Func<IContext, object>>(
                    Expression.New(ctorInfo, getInstanceCallExpressions),
                    contextParameter);

    return lambda.Compile();
}
Sagi
  • 8,972
  • 3
  • 33
  • 41