9

コード内のいくつかのアクセシビリティ関連のものをクリーンアップしようとしていたところ、Unity の依存性注入がうっかり壊れてしまいました。しばらくして、実際には DLL の外部に公開したくないいくつかのパブリック プロパティを internal にマークしたことに気付きました。その後、例外が発生し始めました。

したがって、Unity で [Dependency] 属性を使用すると、パブリック プロパティに対してのみ機能するようです。Unity アセンブリには内部およびプライベートの props が表示されないため、これは理にかなっていると思いますが、Unity以外の誰も設定したくない、または設定できないようにしたくないパブリックプロパティがたくさんあるのは本当に汚いと感じます。

Unity に内部プロパティまたはプライベート プロパティも設定させる方法はありますか?

ここに合格したい単体テストがあります。現在、public prop テストのみがパスします。

    [TestFixture]
public class UnityFixture
{
    [Test]
    public void UnityCanSetPublicDependency()
    {
        UnityContainer container = new UnityContainer();
        container.RegisterType<HasPublicDep, HasPublicDep>();
        container.RegisterType<TheDep, TheDep>();

        var i = container.Resolve<HasPublicDep>();
        Assert.IsNotNull(i);
        Assert.IsNotNull(i.dep);
    }

    [Test]
    public void UnityCanSetInternalDependency()
    {
        UnityContainer container = new UnityContainer();
        container.RegisterType<HasInternalDep, HasInternalDep>();
        container.RegisterType<TheDep, TheDep>();

        var i = container.Resolve<HasInternalDep>();
        Assert.IsNotNull(i);
        Assert.IsNotNull(i.dep);
    }

    [Test]
    public void UnityCanSetPrivateDependency()
    {
        UnityContainer container = new UnityContainer();
        container.RegisterType<HasPrivateDep, HasPrivateDep>();
        container.RegisterType<TheDep, TheDep>();

        var i = container.Resolve<HasPrivateDep>();
        Assert.IsNotNull(i);
        Assert.IsNotNull(i.depExposed);
    }
}

public class HasPublicDep
{
    [Dependency]
    public TheDep dep { get; set; }
}

public class HasInternalDep
{
    [Dependency]
    internal TheDep dep { get; set; }
}

public class HasPrivateDep
{
    [Dependency]
    private TheDep dep { get; set; }

    public TheDep depExposed
    {
        get { return this.dep; }
    }
}

public class TheDep
{
}

更新しました:

渡されたプロパティを設定するコールスタックに気付きました:

UnityCanSetPublicDependency()
--> Microsoft.Practices.Unity.dll
--> Microsoft.Practices.ObjectBuilder2.dll
--> HasPublicDep.TheDep.set()

したがって、少なくとも内部バージョンを機能させるために、これらをアセンブリのプロパティに追加しました。

[assembly: InternalsVisibleTo("Microsoft.Practices.Unity")]
[assembly: InternalsVisibleTo("Microsoft.Practices.Unity.Configuration")]
[assembly: InternalsVisibleTo("Microsoft.Practices.ObjectBuilder2")]

ただし、変化なし。Unity/ObjectBuilder はまだ内部プロパティを設定しません

4

8 に答える 8

6

別の解決策は、依存関係をクラスに渡すメソッドで[InjectionMethod]を使用することです。

public class MyClass {
private ILogger logger;

[InjectionMethod]
public void Init([Dependency] ILogger logger)
{
    this.logger = logger;

...等


そしてそれを呼び出す:

container.BuildUp<MyClass>(instanceOfMyClass);

Unityからの依存関係でInitを呼び出します。

問題を完全には解決しませんでした、私は知っています...しかし

:-) J

于 2009-02-17T11:16:30.627 に答える
5

プロパティが取得専用の場合は、プロパティ インジェクションではなくコンストラクタ インジェクションを使用する方が理にかなっています。

Unityリフレクションを使用してプライベート メンバーまたは内部メンバーを設定した場合、コード アクセス セキュリティの制約を受けることになります。具体的には、信頼性の低い環境では機能しません。

于 2009-01-23T16:50:49.077 に答える
5

質問自体が誤解のようです。

コアステートメントについて:

Unity 以外の誰も設定したくない、または設定できるようにしたくない一連のパブリック プロパティ。

単体テストでそれらを設定したいですか、それとも依存関係のモックをどのように渡しますか? 単体テストがなくても、(Unity のいくつかの魔法を除いて) 何も設定できない依存関係があるというのは奇妙な考えです。コードをサポート ツールに大きく依存させたいですか?

また、コードは実装ではなくインターフェイスに依存する必要があるため (SOLID 原則の 1 つ)、パブリック プロパティを持つことはまったく問題ではありません。この原則に従わない場合、Unity を使用する理由はありません。確かに、インターフェースで依存関係を宣言しないので、消費するクラスはそれらについて知りません。

コンストラクター注入を使用する方がよいと既に言われていますが、プロパティ注入にも優れた点があります。より少ない変更で新しい依存関係を追加できます (特に、既存の単体テストをまったく変更せずに、新しいものだけを追加することができます)。

于 2012-10-16T13:03:58.287 に答える
3

Enterprise Library 5.0 の更新

rally52rs が警告したように、EntLib5.0 へのアップグレードは彼の実装を壊します。Rally と同じアプローチを使用して、新しいコード ベースを検討し、InternalConstructorSelectorPolicy の次の 5.0 互換バージョンを作成しました。

私のバージョンでは、FindLongestConstructor メソッドの内部コンストラクターに限定されていることに注意してください。この点で、私のコードは機能的に Rally のものとは異なります。

public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy, IBuilderPolicy 
{
    private IDependencyResolverPolicy CreateResolver(ParameterInfo parameter)
    {
        List<DependencyResolutionAttribute> attrs = parameter.GetCustomAttributes(false).OfType<DependencyResolutionAttribute>().ToList<DependencyResolutionAttribute>();
        if (attrs.Count > 0)
        {
            return attrs[0].CreateResolver(parameter.ParameterType);
        }
        return new NamedTypeDependencyResolverPolicy(parameter.ParameterType, null);
    }

    private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination, ConstructorInfo ctor)
    {
        SelectedConstructor result = new SelectedConstructor(ctor);
        foreach (ParameterInfo param in ctor.GetParameters())
        {
            string key = Guid.NewGuid().ToString();
            IDependencyResolverPolicy policy = this.CreateResolver(param);
            resolverPolicyDestination.Set<IDependencyResolverPolicy>(policy, key);
            DependencyResolverTrackerPolicy.TrackKey(resolverPolicyDestination, context.BuildKey, key);
            result.AddParameterKey(key);
        }
        return result;
    }

    private static ConstructorInfo FindInjectionConstructor(Type typeToConstruct)
    {
        ConstructorInfo[] injectionConstructors = typeToConstruct
            .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where<ConstructorInfo>(delegate(ConstructorInfo ctor)
        {
            return ctor.IsDefined(typeof(InjectionConstructorAttribute), true);
        }).ToArray<ConstructorInfo>();
        switch (injectionConstructors.Length)
        {
            case 0:
                return null;

            case 1:
                return injectionConstructors[0];
        }
        throw new InvalidOperationException(string.Format("Multiple constructors found for {0}" , typeToConstruct.Name ));
    }

    private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
    {
        var constructors =
            Array.FindAll(
                typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic),
                ctor => !ctor.IsFamily && !ctor.IsPrivate);  //Filter out protected and private constructors

        Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer());
        switch (constructors.Length)
        {
            case 0:
                return null;

            case 1:
                return constructors[0];
        }
        int paramLength = constructors[0].GetParameters().Length;
        if (constructors[1].GetParameters().Length == paramLength)
        {
            throw new InvalidOperationException(string.Format("Ambiguous constructor found for {0}", typeToConstruct.Name));
        }
        return constructors[0];
    }

    public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination)
    {
        Type typeToConstruct = context.BuildKey.Type;
        ConstructorInfo ctor = FindInjectionConstructor(typeToConstruct) ?? FindLongestConstructor(typeToConstruct);
        if (ctor != null)
        {
            return this.CreateSelectedConstructor(context, resolverPolicyDestination, ctor);
        }
        return null;
    }

    // Nested Types
    private class ConstructorLengthComparer : IComparer<ConstructorInfo>
    {
        // Methods
        public int Compare(ConstructorInfo x, ConstructorInfo y)
        {
            return (y.GetParameters().Length - x.GetParameters().Length);
        }
    }
}
于 2010-06-09T19:19:13.093 に答える
2

リフレクターをいろいろと調べた後、私はこれを理解しました。デフォルトでは、コンストラクター注入呼び出し用のコンストラクターを見つけるコードは次のように呼び出します。

ConstructorInfo[] constructors = typeToConstruct.GetConstructors()

BindingFlags がない場合、パブリック コンストラクターのみが検出されます。(リフレクターからのコピー/貼り付けのように) いくつかのトリックを使用して、デフォルトの実装とすべて同じことを行う UnityContainerExtension を作成できますが、GetConstructors() への呼び出しを次のように変更します。

ConstructorInfo[] constructors = typeToConstruct..GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)

次に、拡張機能を Unity コンテナーに追加します。実装された拡張機能は約 100 行のコードなので、ここには貼り付けませんでした。欲しい人いたら教えて...

新しい作業テスト ケース。Unity で作成されたクラスはすべて内部クラスになっていることに注意してください。

[TestFixture]
public class UnityFixture
{
    [Test]
    public void UnityCanSetInternalDependency()
    {
        UnityContainer container = new UnityContainer();
        container.AddNewExtension<InternalConstructorInjectionExtension>();
        container.RegisterType<HasInternalDep, HasInternalDep>();
        container.RegisterType<TheDep, TheDep>();

        var i = container.Resolve<HasInternalDep>();
        Assert.IsNotNull(i);
        Assert.IsNotNull(i.dep);
    }
}


internal class HasInternalDep
{
    internal HasInternalDep(TheDep dep)
    {
        this.dep = dep;
    }

    internal TheDep dep { get; set; }
}

internal class TheDep
{
}

非公開プロパティを解決するために同じことを行う拡張機能を作成できると確信していますが、そのコードははるかに複雑でした:)

于 2009-01-23T18:51:11.457 に答える
1

@ rally25rs、投稿は2年以上前ですが、まだ上位にランクされています(ビュー/グーグルなど)ので、2セントを追加すると思いました..私は同じ問題を抱えていて、最終的にこの解決策を選択しました:UnityContainerと内部コンストラクター。これはコメントとして意図されていますが、まだコメントを投稿することはできません。

あなたはおそらくこれをすでに見て知っているでしょうが、それでも他の人が見ているのに役立つかもしれません:InternalsVisibleTo()属性は機能するはずがありません - それは Unity があなたのクラスを直接呼び出していないからです. 代わりに、リフレクションを使用してType. もちろん、Type属性が存在する結果として が変更されることはありません。受信側で内部可視化などの利点を「楽しむ」には、内部 c'tor (またはプロパティ) を明示的に呼び出す必要があります。

于 2011-09-30T02:12:21.663 に答える
0

これは私の内部コンストラクターインジェクター拡張クラスです:

大きな潜在的な問題:これの 99% は、Unity バージョン 4.1.0.0 の .NET リフレクターからの Unity コードのコピー/貼り付けです。Unity の新しいバージョンでは、実装が変更されてこの拡張機能が壊れたり、不安定なエラーが発生したりする可能性があります。あなたは警告されています!

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

namespace MyApp.Unity.Configuration
{
    /// <summary>
    /// This extension changes the behavior of Unity constructor injection to allow the use of non-public constructors.
    /// By default, Unity/ObjectBuilder would call Type.GetConstructors() to get the constructors. With the default binding
    /// flags, this only returns public constructors.
    /// The code here is 99% copy/paste from Reflector's dissassembly of the default Unity/OB implementation.
    /// My only change was to add binding flags to get all constructors, not just public ones.
    /// For more info, see: Microsoft.Practices.Unity.ObjectBuilder.DefaultUnityConstructorSelectorPolicy
    /// </summary>
    public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy
    {
        protected IDependencyResolverPolicy CreateResolver(ParameterInfo param)
        {
            List<DependencyResolutionAttribute> list = new List<DependencyResolutionAttribute>(Sequence.OfType<DependencyResolutionAttribute>(param.GetCustomAttributes(false)));
            if (list.Count > 0)
            {
                return list[0].CreateResolver(param.ParameterType);
            }
            return new NamedTypeDependencyResolverPolicy(param.ParameterType, null);
        }

        private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, ConstructorInfo ctor)
        {
            SelectedConstructor constructor = new SelectedConstructor(ctor);
            foreach (ParameterInfo info in ctor.GetParameters())
            {
                string buildKey = Guid.NewGuid().ToString();
                IDependencyResolverPolicy policy = this.CreateResolver(info);
                context.PersistentPolicies.Set<IDependencyResolverPolicy>(policy, buildKey);
                DependencyResolverTrackerPolicy.TrackKey(context.PersistentPolicies, context.BuildKey, buildKey);
                constructor.AddParameterKey(buildKey);
            }
            return constructor;
        }

        private ConstructorInfo FindInjectionConstructor(Type typeToConstruct)
        {
            ConstructorInfo[] infoArray = Array.FindAll<ConstructorInfo>(typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), delegate(ConstructorInfo ctor)
            {
                return ctor.IsDefined(typeof(InjectionConstructorAttribute), true);
            });
            switch (infoArray.Length)
            {
                case 0:
                    return null;

                case 1:
                    return infoArray[0];
            }
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.MultipleInjectionConstructors", new object[] { typeToConstruct.Name }));
        }

        private ConstructorInfo FindLongestConstructor(Type typeToConstruct)
        {
            ConstructorInfo[] constructors = typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer());
            switch (constructors.Length)
            {
                case 0:
                    return null;

                case 1:
                    return constructors[0];
            }
            int length = constructors[0].GetParameters().Length;
            if (constructors[1].GetParameters().Length == length)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.AmbiguousInjectionConstructor", new object[] { typeToConstruct.Name, length }));
            }
            return constructors[0];
        }

        public virtual SelectedConstructor SelectConstructor(IBuilderContext context)
        {
            Type typeToConstruct = BuildKey.GetType(context.BuildKey);
            ConstructorInfo ctor = this.FindInjectionConstructor(typeToConstruct) ?? this.FindLongestConstructor(typeToConstruct);
            if (ctor != null)
            {
                return this.CreateSelectedConstructor(context, ctor);
            }
            return null;
        }

        // Nested Types
        private class ConstructorLengthComparer : IComparer<ConstructorInfo>
        {
            // Methods
            public int Compare(ConstructorInfo x, ConstructorInfo y)
            {
                return (y.GetParameters().Length - x.GetParameters().Length);
            }
        }
    }

    /// <summary>
    /// Registeres the InternalConstructorSelectorPolicy with the Unity container.
    /// </summary>
    public class InternalConstructorInjectionExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            this.Context.Policies.SetDefault(typeof(IConstructorSelectorPolicy), new InternalConstructorSelectorPolicy());
        }
    }
}
于 2009-06-26T17:43:27.530 に答える