1

我是 Unity 的新手,我很难弄清楚如何扩展 Unity 的汽车工厂概念。Unity 提供了开箱即用的能力,可以使用 Func 代替参数本身来创建工厂。像那样:

public class Foo
{
    private readonly Func<IBar> _barFactory;

    public Foo(Func<IBar> barFactory)
    {
        _bar = barFactory;
    }
}

问题是,工厂创建的类在某些地方需要一些参数,而我只会在运行时知道这些参数。除此之外,这些参数的数量和类型因类而异。

我正在寻找类似于Autofac DelegateFactry的东西,但我想保持 Unity 的感觉。所以,我想让 Unity 像这样工作:

public class Foo
{
    private readonly Func<int, string, IBar> _barFactory;

    public Foo(Func<int, string, IBar> barFactory)
    {
        _bar = barFactory;
    }
}

上面的代码不起作用,因为 Unity 确实可以使用该构造函数,但这正是我正在寻找的。

我尝试过使用 BuilderStrategy 但它归结为表达式或 IL 生成。在走这条路之前,我想检查一下其他选项。

有没有足够有Unity经验的人可以帮助我?

以下是我的限制:

  1. 保持Unity使用Func的理念
  2. 不需要在容器中注册每一个 Func
  3. 构造函数应该接受从 Func 到 Func 的 Func(已经处理了 Func)
  4. 更改容器不是一种选择

我希望我足够清楚。如果我不是,请告诉我。

编辑1:我知道我可以直接使用抽象工厂。但是,第一个目标是保持 Unity 的感觉。

4

2 回答 2

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<>to的构造函数Fun<,,,,>。例如,现在处理以下构造函数(假设可以解析IFoo):

public class Bar
{
    private readonly Func<int, string, IFoo> _fooFactory;

    public Bar(Func<int, string, IFoo> fooFactory)
    {
        _fooFactory = fooFactory;
    }
}

让我知道是否有任何问题。

希望这可以帮助。

于 2012-12-01T16:39:51.853 回答
0

您是否考虑过使用 Unity 创建一个委托并注册它的实例?这样你就可以命名参数和对这些参数的注释,以及委托本身。这样您就不需要创建构建策略,并且您的代码将更具可读性。

于 2014-07-02T23:30:47.747 回答