我是Unity的新手,我很难弄清楚如何扩展Unity的Auto Factory概念。 Unity提供了开箱即用的功能,可以使用Func代替参数本身创建工厂。像那样:
public class Foo
{
private readonly Func<IBar> _barFactory;
public Foo(Func<IBar> barFactory)
{
_bar = barFactory;
}
}
问题是,工厂创建的类需要一些参数,我只会在运行时知道这些参数。除此之外,这些参数的数量和类型因班级而异。
我正在寻找的东西与Autofac DelegateFacotry类似,但我想保持Unity的感觉。所以,我想让Unity像这样工作:
public class Foo
{
private readonly Func<int, string, IBar> _barFactory;
public Foo(Func<int, string, IBar> barFactory)
{
_bar = barFactory;
}
}
上面的代码不起作用,因为Unity确实可以使用该构造函数,但它正是我正在寻找的。 p>
我尝试过使用BuilderStrategy但它归结为Expression或IL代。在走这条路之前,我想检查其他选项。
是否有任何一位经验丰富的Unity可以帮助我?
以下是我的约束:
我希望我足够清楚。如果我不是,请告诉我。
编辑1 :我知道我可以直接使用抽象工厂。但是,第一个目标是保持Unity的感觉。
答案 0 :(得分:1)
我不想回答我自己的问题,但我能够以某种方式解决问题,我相信解决方案足够好,其他人可能会感兴趣。我看了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;
}
}
如果有任何问题,请告诉我。
希望这有帮助。
答案 1 :(得分:0)
您是否考虑过使用Unity创建委托并注册和实例?这样,您就可以为这些参数命名参数和注释,以及委托本身。然后,您不需要创建构建策略,并且您的代码将更具可读性。