web-dev-qa-db-ja.com

Microsoft Unity。コンストラクタで特定のパラメータを指定するにはどうすればよいですか?

Microsoft Unityを使用しています。インターフェイスICustomerServiceとその実装CustomerServiceがあります。次のコードを使用して、Unityコンテナーにそれらを登録できます。

_container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager());
_

CustomerServiceのコンストラクターに特定のパラメーターがある場合(例:_ISomeService1_)、次のコードを使用します(_SomeService1_を指定する必要があります)。

_container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1()));
_

ここでは問題ありません。

この問題は、CustomerServiceクラスのコンストラクターに2つのパラメーター(前の例のように1つのパラメーターではない)がある場合に発生します(例:_ISomeService1_および_ISomeService2_)。次のコードを使用すると問題なく動作します:container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));

問題は、2番目のパラメーターにSomeService2()を指定したくないことです。最初のパラメーターのみを指定したい-SomeService1()。しかし、パラメーターをまったく指定しないか、両方指定する必要があるというエラーが表示されます。

コンストラクタの最初のパラメータのみを指定するにはどうすればよいですか?

34
Andrei M

あなたの最良の答えは、実際にコンテナを使用することです。

あなたがやっていることは、「このタイプを構築するときは、オブジェクトのこの特定のインスタンスを使用すること」です。これは、インスタンスを構築するコンテナの機能を利用していません。代わりに、コンテナにIService1とIService2を登録する必要があります。次に、これらの依存関係を解決するようにコンテナに指示します。

次のようになります。

container.RegisterType<IService1, SomeService1>();
container.RegisterType<IService2, SomeService2>();

これにより、「IService1型の依存関係がある場合は常に、SomeService1型の新しいオブジェクトを更新してそれを渡す」ことがIService2に対しても同様に行われます。

したがって、次に、ICustomerServiceについて何をするかをコンテナに伝える必要があります。最も一般的には、次のようにします。

container.RegisterType<ICustomerService, CustomerService>(
    // Note, don't need to explicitly say transient, that's the default
    new InjectionConstructor(new ResolvedParameter<IService1>(),
        new ResolvedParameter<IService2>()));

これはコンテナに指示します。ICustomerServiceを解決するときは、IService1とIService2を受け取るコンストラクターを使用してCustomerServiceのインスタンスを新しく作成します。これらのパラメーターを取得するには、コンテナーにコールバックして、それらのタイプを解決します。

これは少し冗長で一般的なケースなので、いくつかのショートカットがあります。まず、次のように、新しいResolvedParameterを実行する代わりに、Typeオブジェクトを渡すことができます。

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), typeof (IService2)));

別の略記として、CustomerServiceにコンストラクターが1つしかない場合、または呼び出したいものが最大のパラメーターリストを取得するコンストラクターである場合は、InjectionConstructorを完全に省略することができます。構成。

container.RegisterType<ICustomerService, CustomerService>();

使用するフォームは通常、コンテナーを介してサービスを解決するのではなく、コンストラクターパラメーターに特定の値を渡す場合に使用されます。

元の質問に答えるために、まあ、あなたが言ったことを正確に行うことはできません。コンストラクター・パラメーターには、ある種の値が必要です。ただし、そこには他に何でも置くことができますが、通常はnullが機能します。

2つの形式を混在させることもできます。たとえば、IService1を解決してIService2パラメータにnullを渡す場合は、次のようにします。

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), null));

*編集*

以下のコメントに基づいて、実際に必要なのは、名前付き登録という別の機能です。

基本的に、IService1の2つの実装とIService2の1つの実装があります。だから、あなたができることはそれらの両方を登録し、次にどちらを使うべきかをコンテナに伝えます。

まず、2番目の実装を登録するには、明示的な名前を付ける必要があります。

container.RegisterType<IService1, OtherService1Impl>("other");

次に、コンテナにIService1を解決するように指示できますが、名前は使用できます。これが、ResolvedParameterタイプが存在する主な理由です。 IService2のデフォルトが必要なだけなので、省略形としてtypeof()を使用できます。パラメータには両方のタイプを指定する必要がありますが、特定の値は必要ありません。それが理にかなっている場合。

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new ResolvedParameter<IService1>("other"), typeof(IService2));

それはあなたが必要なことをするはずです。

66
Chris Tavares

Chris Tavaresの回答の代わりに、コンテナーに2番目のパラメーターのみを解決させることができます。

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new SomeService1(), new ResolvedParameter<IService2>());
14
onof

クリス・タバレスは多くの情報で良い答えを出しました。

注入する多くのパラメーターがある場合、これらは通常、Unityで解決できるインターフェースまたはインスタンスです(異なるネットテクニックを使用)。しかし、自動的に解決できないパラメータを1つだけ提供したい場合はどうでしょうか。ファイル名の文字列?

次に、すべてのtypeof(IMyProvider)と1つの文字列またはインスタンスを提供する必要があります。しかし、正直に言うと、Unityはタイプを提供するだけで済みます。Unityはすでに最高の俳優を選択する戦略を持っているためです。

だから私はInjectionConstructorの代わりをコーディングしました:

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;

namespace Microsoft.Practices.Unity
{
    /// <summary>
    /// A class that holds the collection of information for a constructor, 
    /// so that the container can be configured to call this constructor.
    /// This Class is similar to InjectionConstructor, but you need not provide
    /// all Parameters, just the ones you want to override or which cannot be resolved automatically
    /// The given params are used in given order if type matches
    /// </summary>
    public class InjectionConstructorRelaxed : InjectionMember
    {
        private List<InjectionParameterValue> _parameterValues;

        /// <summary>
        /// Create a new instance of <see cref="InjectionConstructor"/> that looks
        /// for a constructor with the given set of parameters.
        /// </summary>
        /// <param name="parameterValues">The values for the parameters, that will
        /// be converted to <see cref="InjectionParameterValue"/> objects.</param>
        public InjectionConstructorRelaxed(params object[] parameterValues)
        {
            _parameterValues = InjectionParameterValue.ToParameters(parameterValues).ToList();
        }

        /// <summary>
        /// Add policies to the <paramref name="policies"/> to configure the
        /// container to call this constructor with the appropriate parameter values.
        /// </summary>
        /// <param name="serviceType">Interface registered, ignored in this implementation.</param>
        /// <param name="implementationType">Type to register.</param>
        /// <param name="name">Name used to resolve the type object.</param>
        /// <param name="policies">Policy list to add policies to.</param>
        public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
        {
            ConstructorInfo ctor = FindExactMatchingConstructor(implementationType);
            if (ctor == null)
            {
                //if exact matching ctor not found, use the longest one and try to adjust the parameters.
                //use given Params if type matches otherwise use the type to advise Unity to resolve later
                ctor = FindLongestConstructor(implementationType);
                if (ctor != null)
                {
                    //adjust parameters
                    var newParams = new List<InjectionParameterValue>();
                    foreach (var parameter in ctor.GetParameters())
                    {
                        var injectionParameterValue =
                            _parameterValues.FirstOrDefault(value => value.MatchesType(parameter.ParameterType));
                        if (injectionParameterValue != null)
                        {
                            newParams.Add(injectionParameterValue);
                            _parameterValues.Remove(injectionParameterValue);
                        }
                        else
                            newParams.Add(InjectionParameterValue.ToParameter(parameter.ParameterType));
                    }
                    _parameterValues = newParams;
                }
                else
                {
                    throw new InvalidOperationException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            "No constructor found for type {0}.",
                            implementationType.GetTypeInfo().Name));
                }
            }
            policies.Set<IConstructorSelectorPolicy>(
                new SpecifiedConstructorSelectorPolicy(ctor, _parameterValues.ToArray()),
                new NamedTypeBuildKey(implementationType, name));
        }



        private ConstructorInfo FindExactMatchingConstructor(Type typeToCreate)
        {
            var matcher = new ParameterMatcher(_parameterValues);
            var typeToCreateReflector = new ReflectionHelper(typeToCreate);

            foreach (ConstructorInfo ctor in typeToCreateReflector.InstanceConstructors)
            {
                if (matcher.Matches(ctor.GetParameters()))
                {
                    return ctor;
                }
            }

            return null;
        }

       private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
        {
            ReflectionHelper typeToConstructReflector = new ReflectionHelper(typeToConstruct);

            ConstructorInfo[] constructors = typeToConstructReflector.InstanceConstructors.ToArray();
            Array.Sort(constructors, new ConstructorLengthComparer());

            switch (constructors.Length)
            {
                case 0:
                    return null;

                case 1:
                    return constructors[0];

                default:
                    int paramLength = constructors[0].GetParameters().Length;
                    if (constructors[1].GetParameters().Length == paramLength)
                    {
                        throw new InvalidOperationException(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                "The type {0} has multiple constructors of length {1}. Unable to disambiguate.",
                                typeToConstruct.GetTypeInfo().Name,
                                paramLength));
                    }
                    return constructors[0];
            }
        }
        private class ConstructorLengthComparer : IComparer<ConstructorInfo>
        {
            /// <summary>
            /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
            /// </summary>
            /// <param name="y">The second object to compare.</param>
            /// <param name="x">The first object to compare.</param>
            /// <returns>
            /// Value Condition Less than zero is less than y. Zero equals y. Greater than zero is greater than y.
            /// </returns>
            [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class")]
            public int Compare(ConstructorInfo x, ConstructorInfo y)
            {
                Guard.ArgumentNotNull(x, "x");
                Guard.ArgumentNotNull(y, "y");

                return y.GetParameters().Length - x.GetParameters().Length;
            }
        }
    }
}

使用法:

container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
    new SomeService1("with special options")
    //, new SomeService2() //not needed, normal unity resolving used
    //equivalent to: , typeof(SomeService2)
    ));
9
BerndK

コンテナ階層を使用できます。共通の実装を親コンテナに登録します。このインスタンスは、マスターコンテナを通じて解決された場合に解決されます。次に、子コンテナを作成し、代替実装を子コンテナに登録します。この実装は、子コンテナーを介して解決された場合に解決します。つまり、子コンテナーでの登録は、親コンテナーでの登録をオーバーライドします。

次に例を示します。

public interface IService {}

public interface IOtherService {}

// Standard implementation of IService
public class StandardService : IService {}

// Alternative implementaion of IService
public class SpecialService : IService {}

public class OtherService : IOtherService {}

public class Consumer
{
    public Consumer(IService service, IOtherService otherService)
    {}
}

private void Test()
{
    IUnityContainer parent = new UnityContainer()
        .RegisterType<IService, StandardService>()
        .RegisterType<IOtherService, OtherService>();

    // Here standardWay is initialized with StandardService as IService and OtherService as IOtherService
    Consumer standardWay = parent.Resolve<Consumer>();

    // We construct child container and override IService registration
    IUnityContainer child = parent.CreateChildContainer()
        .RegisterType<IService, SpecialService>();

    // And here specialWay is initialized with SpecialService as IService and still OtherService as IOtherService
    Consumer specialWay = child.Resolve<Consumer>();

    // Profit!
}

コンテナー階層を使用すると、コンストラクターのパラメーターの数について何も知ることができないことに注意してください。パラメーターの数とそのタイプにバインドされている限り、IoCの全機能を使用できないためです。知っているほど、よくなります。

2
Dmitrii Lobanov

2番目のパラメーターをデフォルトでコンストラクターに設定できます(例:=null)または、オーバーロードすることにより、2つのパラメーターコンストラクターに加えて、単一のパラメーターコンストラクターを提供します。

0