Unity Framework 의존성 기능은 공개 속성에만 효과가 있습니까?

StackOverflow https://stackoverflow.com/questions/473575

  •  19-08-2019
  •  | 
  •  

문제

코드에서 액세스 가능성을 정리하려고했고 실수로 Unity Feence Injection을 깨뜨 렸습니다. 잠시 후 나는 DLL 밖에서 내부에 노출되기를 원하지 않는 공개 속성을 표시했다는 것을 깨달았습니다. 그런 다음 예외를 얻기 시작했습니다.

따라서 Unity에서 [종속성] 속성을 사용하는 것은 공개 속성에만 작동하는 것 같습니다. 내부 및 개인 소품이 Unity Assembly에 보이지 않기 때문에 의미가 있다고 생각하지만 정말 더럽습니다 당신이 많은 공개 속성을 갖는 것 절대 Unity 이외의 다른 사람이 설정하거나 설정할 수 있기를 원합니다.

Unity가 내부 또는 개인 속성을 설정하게하는 방법이 있습니까?

다음은 패스를보고 싶은 단위 테스트입니다. 현재 공개 소품 테스트 만 통과합니다.

    [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는 여전히 내부 속성을 설정하지 않습니다

도움이 되었습니까?

해결책 5

리플렉터에서 멍청이가 끝난 후, 나는 이것을 알아 냈습니다. 기본적으로 생성자 주입을위한 생성자를 찾는 코드는 다음과 같습니다.

ConstructorInfo[] constructors = typeToConstruct.GetConstructors()

바인딩 플래그가 없으면 공개 생성자 만 감지합니다. 리플렉터의 복사/페이스트에서와 같이 약간의 속임수를 사용하면 기본 구현과 동일한 작업을 수행하는 UnityContainereLextension을 만들 수 있지만 호출을 getConstructors ()로 변경할 수 있습니다.

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

그런 다음 확장을 유니티 컨테이너에 추가하십시오. 구현 된 확장은 ~ 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
{
}

나는 비공개 속성을 해결하기 위해 똑같이 연장 할 수 있다고 확신하지만, 그 코드는 훨씬 더 복잡했습니다 :)

다른 팁

또 다른 해결책은 종속성을 클래스에 전달하는 방법에 [주입 메드]를 사용하는 것입니다.

public class MyClass {
private ILogger logger;

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

...등


그리고 그것을 부르기 :

container.BuildUp<MyClass>(instanceOfMyClass);

Unity의 의존성과 함께 Init을 호출합니다.

문제를 제대로 해결하지 못했습니다.

:-) 제이

속성이 전용되면 사용하는 것이 더 합리적입니다. 대조적 주입 재산 주입보다는.

Unity라면 했다 반사를 사용하여 개인 또는 내부 멤버를 설정하면 코드 액세스 보안 제약 조건이 적용됩니다. 구체적으로, 그것은 저 트러스트 환경에서는 작동하지 않을 것입니다.

질문 자체는 오해 인 것 같습니다.

핵심 설명과 관련하여 :

Unity 이외의 다른 사람을 설정하거나 설정하기를 원하지 않는 많은 공개 속성.

단위 테스트에서 설정하거나 의존성 모의를 어떻게 통과 하시겠습니까? 단위 테스트가 없더라도 단일의 마법을 제외하고는 아무것도 설정할 수없는 의존성을 갖는 것이 이상한 생각입니다. 코드가 지원 도구에 크게 의존하기를 원하십니까?

또한 코드가 구현이 아닌 인터페이스 (견고한 원칙 중 하나)에 의존해야하기 때문에 공개 속성을 갖는 것은 전혀 문제가되지 않습니다. 이 원칙을 따르지 않는다면 - Unity를 사용할 이유가 없습니다. 확실히 당신은 인터페이스의 종속성을 선언하지 않으므로 소비 클래스는 그들에 대해 알지 못합니다.

당신은 이미 생성자 주입을 사용하는 것이 더 낫다는 말을 들었지만 재산 주입도 그 아름다움을 가지고 있습니다. 수정이 적은 새로운 종속성을 추가 할 수 있습니다 (특히 기존 단위 테스트를 전혀 변경하지 않고 새 항목 만 추가 할 수 있습니다).

Enterprise Library 5.0에 대한 업데이트

Rally52RS가 경고 할 수 있으므로 Entlib5.0으로의 업그레이드가 그의 구현을 중단합니다. Rally와 같은 접근 방식을 사용하여 새 코드 기반에 반영되었고 다음 5.0 호환 버전의 내부 ConstructorselectorPolicy를 작업했습니다.

내 버전은 특히 FindLongestConstructor 메소드의 내부 생성자로 제한됩니다. 이 시점에서 내 코드는 랠리와 기능적으로 다릅니다..

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);
        }
    }
}

@rally25rs, 게시물이 2 년 이상이지만 여전히 높은 순위가 높았으므로 (보기/Google 등) 2 센트를 추가 할 것이라고 생각했습니다. 같은 문제가 있었고 결국이 솔루션을 선택했습니다. UnityContainer 및 내부 생성자. 이것은 의견을 의미하지만 아직 댓글을 게시 할 수 없습니다.

당신은 아마 이미 이것을보고 알고있을 것입니다. 여전히 다른 사람이 보는 사람에게 사용될 수 있습니다. InternalsVisibleTo() 속성은 결코 효과가 없어야합니다. 그것은 Unity가 수업을 직접 호출하지 않기 때문입니다. 대신, 반사를 사용하고 검사합니다 Type. 물론 Type 속성이있는 결과로 변경되지 않습니다. 수신 측면에서 내부 눈에 보이는 내부의 이점을 "즐기려면 내부 c'tor (또는 재산)를 명시 적으로 호출해야합니다.

Kent B의 답변을 바탕으로 공공 수업에서 작동하는 생성자 주입을 사용하도록 변경했습니다. 그러나 루트 문제는 여전히 존재하며, Unity가 할당하거나 할당하고 싶은 것이 공개되어야합니다. 여기에는 수업 자체가 포함됩니다.

새로운 단위 테스트 :

    [TestFixture]
public class UnityFixture
{
    [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);
    }
    }

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

    private TheDep _Dep;
        internal TheDep dep
        {
            get { return _Dep; }
        }
}

internal class TheDep
{
}
}

어셈블리 속성으로 :

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

오류로 실패합니다.

The type HasInternalDep does not have an accessible constructor.
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, String name)

따라서 전반적으로 Unity를 사용하려면 기본적으로 모든 것을 공개적으로 표시해야합니다. 유틸리티/라이브러리 .dll에 정말 못생긴 ...

이것은 내 내부 생성자 인젝터 확장 클래스입니다.

큰 잠재적 문제 : 이 중 99%는 Unity 버전 4.1.0.0의 .NET Reflector의 Unity 코드의 복사/페이스트입니다. 최신 버전의 Unity는 구현을 변경 하고이 확장을 중단하거나 Flakey 오류를 유발할 수 있습니다. 당신은 경고합니다!

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());
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top