Я не люблю отвечать на свои вопросы, но мне удалось каким-то образом решить проблему, и я считаю, что решение достаточно хорошее, чтобы другие могли заинтересоваться. Я посмотрел исходный код Unity и получил оттуда основную идею. Я также прочитал пару сообщений о Unity. Вот:
Во-первых, мне пришлось создать класс, унаследованный от IBuildPlanPolicy
. Это длинно, потому что я оставил некоторые вспомогательные классы внутри самого класса:
public class AutomaticFactoryBuilderPolicy : IBuildPlanPolicy
{
private readonly Dictionary<Type, Type> _callables =
new Dictionary<Type, Type>
{
{typeof(Func<,>), typeof(CallableType<,>)},
{typeof(Func<,,>), typeof(CallableType<,,>)},
{typeof(Func<,,,>), typeof(CallableType<,,,>)},
{typeof(Func<,,,,>), typeof(CallableType<,,,,>)}
};
public void BuildUp(IBuilderContext context)
{
if (context.Existing == null)
{
var currentContainer = context.NewBuildUp<IUnityContainer>();
var buildKey = context.BuildKey;
string nameToBuild = buildKey.Name;
context.Existing = CreateResolver(currentContainer, buildKey.Type, nameToBuild);
}
}
private Delegate CreateResolver(IUnityContainer currentContainer,
Type typeToBuild, string nameToBuild)
{
Type[] delegateTypes = typeToBuild.GetGenericArguments();
Type func = typeToBuild.GetGenericTypeDefinition();
Type callable = _callables[func];
Type callableType = callable.MakeGenericType(delegateTypes);
Type delegateType = func.MakeGenericType(delegateTypes);
MethodInfo resolveMethod = callableType.GetMethod("Resolve");
object callableObject = Activator.CreateInstance(callableType, currentContainer, nameToBuild);
return Delegate.CreateDelegate(delegateType, callableObject, resolveMethod);
}
private class CallableType<T1, TResult>
{
private readonly IUnityContainer _container;
private readonly string _name;
public CallableType(IUnityContainer container, string name)
{
_container = container;
_name = name;
}
public TResult Resolve(T1 p1)
{
return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1 }));
}
}
private class CallableType<T1, T2, TResult>
{
private readonly IUnityContainer _container;
private readonly string _name;
public CallableType(IUnityContainer container, string name)
{
_container = container;
_name = name;
}
public TResult Resolve(T1 p1, T2 p2)
{
return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1, p2 }));
}
}
private class CallableType<T1, T2, T3, TResult>
{
private readonly IUnityContainer _container;
private readonly string _name;
public CallableType(IUnityContainer container, string name)
{
_container = container;
_name = name;
}
public TResult Resolve(T1 p1, T2 p2, T3 p3)
{
return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1, p2, p3 }));
}
}
private class CallableType<T1, T2, T3, T4, TResult>
{
private readonly IUnityContainer _container;
private readonly string _name;
public CallableType(IUnityContainer container, string name)
{
_container = container;
_name = name;
}
public TResult Resolve(T1 p1, T2 p2, T3 p3, T4 p4)
{
return _container.Resolve<TResult>(_name, new OrderedParametersResolverOverride(new object[] { p1, p2, p3, p4 }));
}
}
}
Это довольно просто. Хитрость заключается в том, чтобы создать один CallableType
для каждого Func
, который я хочу обработать. Он не такой динамичный, как мне сначала хотелось, но чтобы сделать его более динамичным, я считаю, что мне придется иметь дело либо с IL, либо с деревьями выражений. То, что у меня есть сейчас, меня вполне устраивает.
Во-вторых, Unity обрабатывает параметры по именам, а мне приходилось работать с ними по порядку. Здесь в игру вступает OrderedParametersResolverOverride (этот класс используется в приведенном выше коде. Проверьте CallableType
классов):
public class OrderedParametersResolverOverride : ResolverOverride
{
private readonly Queue<InjectionParameterValue> _parameterValues;
public OrderedParametersResolverOverride(IEnumerable<object> parameterValues)
{
_parameterValues = new Queue<InjectionParameterValue>();
foreach (var parameterValue in parameterValues)
{
_parameterValues.Enqueue(InjectionParameterValue.ToParameter(parameterValue));
}
}
public override IDependencyResolverPolicy GetResolver(IBuilderContext context, Type dependencyType)
{
if (_parameterValues.Count < 1)
return null;
var value = _parameterValues.Dequeue();
return value.GetResolverPolicy(dependencyType);
}
}
Эти два класса имеют дело с Func
созданием. Следующим шагом будет добавление этого компоновщика в конвейер Unity. Нам нужно создать UnityContainerExtension:
public class AutomaticFactoryExtension: UnityContainerExtension
{
protected override void Initialize()
{
var automaticFactoryBuilderPolicy = new AutomaticFactoryBuilderPolicy();
Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy),
automaticFactoryBuilderPolicy,
new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,>)));
Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy),
automaticFactoryBuilderPolicy,
new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,>)));
Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy),
automaticFactoryBuilderPolicy,
new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,,>)));
Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy),
automaticFactoryBuilderPolicy,
new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,,,>)));
}
}
Последняя часть — добавить этот класс в конвейер Unity:
IUnityContainer container = new UnityContainer();
container.AddExtension(new AutomaticFactoryExtension());
В остальном регистрация стандартная.
Теперь можно иметь конструкторы от Func<>
до Fun<,,,,>
. Например, теперь обрабатывается следующий конструктор (при условии, что возможно разрешить IFoo
):
public class Bar
{
private readonly Func<int, string, IFoo> _fooFactory;
public Bar(Func<int, string, IFoo> fooFactory)
{
_fooFactory = fooFactory;
}
}
Дайте мне знать, если есть какие-либо вопросы.
Надеюсь это поможет.
person
Allan
schedule
01.12.2012