Зависимость конструктора StructureMap 4 с именованным экземпляром

Я хочу реализовать шаблон декоратора с помощью StructureMap 4.
Я создал интерфейс IDeveloper и две реализации
CSharpDeveloper, который является декорированным типом, и DeveloperDecorator
, который является декоратором.
Тип декоратора зависит от IDeveloper, а сам класс реализует IDevloper, что означает, что один из типов должен быть именованным экземпляром.
Именованный экземпляр CSharpDeveloper

public interface IDeveloper
{
}

public class CSharpDeveloper : IDeveloper
{
}

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

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

Я хочу зарегистрировать DeveloperDecorator как IDeveloper, чтобы я мог разрешить тип с помощью безымянной функции перегрузки GetInstance

container.GetInstance<IDeveloper>();

и StructureMap сможет разрешить зависимый тип как именованный экземпляр. Что-то вроде этого (концептуальный код)

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

Я вообще не могу использовать универсальные шаблоны, поэтому не могу использовать Ctor<TCtorType>() api.
У меня есть тип с именем TypeMap, который содержит тип службы, конкретный тип и необязательное имя.

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

и я пытался использовать зависимости, но ничего не помогло.

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);
            });
        }
    });
}

Спасибо.


person Sagi    schedule 26.10.2015    source источник


Ответы (1)


Мне удалось это сделать, предоставив методу Use дерево выражений, которое создает экземпляр и разрешает все зависимости с помощью контейнера.

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();
}
person Sagi    schedule 27.10.2015