Unity框架DependencyAttribute仅适用于公共属性?

Cod*_*ike 9 .net unity-container

我试图在我的代码中清理一些可访问性的东西,并无意中破坏了Unity依赖注入.过了一会儿,我意识到我标记了一些我不想在我的DLL之外暴露到内部的公共属性.然后我开始得到例外.

因此,似乎在Unity中使用[Dependency]属性仅适用于公共属性.我认为这是有道理的,因为内部和私人道具对于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
{
}
Run Code Online (Sandbox Code Playgroud)

更新:

我注意到调用堆栈设置传递的属性:

UnityCanSetPublicDependency()
--> Microsoft.Practices.Unity.dll
--> Microsoft.Practices.ObjectBuilder2.dll
--> HasPublicDep.TheDep.set()
Run Code Online (Sandbox Code Playgroud)

因此,为了至少使内部版本工作,我将这些添加到我的程序集的属性中:

[assembly: InternalsVisibleTo("Microsoft.Practices.Unity")]
[assembly: InternalsVisibleTo("Microsoft.Practices.Unity.Configuration")]
[assembly: InternalsVisibleTo("Microsoft.Practices.ObjectBuilder2")]
Run Code Online (Sandbox Code Playgroud)

但是,没有变化.Unity/ObjectBuilder仍然不会设置内部属性

Joh*_*ino 6

另一种解决方案是在将依赖项传递给类的方法中使用[InjectionMethod].

public class MyClass {
private ILogger logger;

[InjectionMethod]
public void Init([Dependency] ILogger logger)
{
    this.logger = logger;
Run Code Online (Sandbox Code Playgroud)

...等等


并称之为:

container.BuildUp<MyClass>(instanceOfMyClass);
Run Code Online (Sandbox Code Playgroud)

这将调用Init与unity的依赖关系.

我不知道解决这个问题......但是

:-) J


Ken*_*art 5

如果属性是get-only,那么使用构造函数注入而不是属性注入更有意义.

如果Unity 确实使用反射来设置私有或内部成员,那么它将受到代码访问安全性约束.具体而言,它不适用于低信任环境.


Kur*_*ich 5

这个问题本身似乎是一种误解。

关于核心声明:

一堆除了Unity之外都不希望任何人设置或能够设置的公共属性。

您想在单元测试中设置它们,还是要如何通过依赖模拟?即使您没有单元测试,也有一个不可设置的依赖项(除了一些Unity魔术以外)是一个奇怪的主意。您是否希望代码非常依赖支持工具?

而且,拥有公共属性根本不是问题,因为您的代码必须取决于接口,而不取决于实现(SOLID原理之一)。如果您不遵循此原则,则没有理由使用Unity。当然,您不会在接口中声明依赖项,因此使用类不了解它们。

您已经被告知使用构造函数注入更好,但是属性注入也有其美。它允许以更少的修改添加新的依赖项(特别是,您可以完全避免更改现有的单元测试,而仅添加新的)。


Cod*_*ike 2

经过一番研究反射镜后,我明白了这一点。默认情况下,为构造函数注入查找构造函数的代码调用:

ConstructorInfo[] constructors = typeToConstruct.GetConstructors()
Run Code Online (Sandbox Code Playgroud)

如果没有 BindingFlags,则只会检测公共构造函数。通过一些技巧(如从反射器复制/粘贴),您可以创建一个 UnityContainerExtension 来执行与默认实现相同的所有操作,但将对 GetConstructors() 的调用更改为:

ConstructorInfo[] constructors = typeToConstruct..GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
Run Code Online (Sandbox Code Playgroud)

然后将扩展添加到 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
{
}
Run Code Online (Sandbox Code Playgroud)

我确信我可以进行扩展来执行相同的操作来解析非公共属性,但该代码要复杂得多:)