1

私は 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はそのコンストラクターで動作するため、上記のコードは機能しませんが、まさに私が探しているものです.

BuilderStrategy を使用してみましたが、それは式または IL 生成のいずれかに要約されます。その道を進む前に、他のオプションを確認したいと思います。

それを手伝ってくれる Unity の経験が豊富な人はいますか?

私が持っている制約は次のとおりです。

  1. Func を使用するという Unity の概念を維持する
  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 }));
        }
    } 

}

それはかなり簡単です。秘訣は、処理したいものごとに1CallableTypeつ作成することです。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これらの 2 つのクラスは作成を扱います。次のステップは、そのビルダーを 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;
    }
}

ご不明な点がございましたらお知らせください。

お役に立てれば。

于 2012-12-01T16:39:51.853 に答える