为什么Unity RegisterType <tfrom,tto =“”>没有正确地重新注册映射

时间:2015-06-03 11:37:06

标签: .net dependency-injection unity-container

我需要注册许多Type映射,但只需要几个我需要使用不同的依赖注入属性重新注册。

使用Unity这似乎是不可能的,它似乎是RegisterType<TFrom, TTo>()

的错误

这是一个单元测试,用于演示第二次调用不会清除先前的依赖关系图

[TestClass]
public class UnityContainerTests
{
    [TestMethod]
    public void UnityShouldCorrectlyResolveReRegisteredType()
    {
        var container = new UnityContainer();

        container.RegisterType<IB, B>(new ContainerControlledLifetimeManager())
                 .RegisterType<IA, A>(new ContainerControlledLifetimeManager(), new InjectionProperty("B", new B()))

            //now re-register mapping without property override, this should 
            //mean that A.B is resolved to our previously registered singleton but it doesnt
            //comment out the line above and notice the test passes

                 .RegisterType<IA, A>(new ContainerControlledLifetimeManager());

        var a = container.Resolve<IA>();
        var b = container.Resolve<IB>();

        Assert.AreEqual(a.B, b);
    }
}

public class A : IA
{
    [Dependency] public IB B { get; set; }
}

public interface IA
{
    IB B { get; set; }
}

public class B : IB { }

public interface IB { }

任何人都知道有任何变通方法吗?

更新我提出了一个问题&#39;在Unity Codeplex项目门户网站上,如果您遇到过这个问题,那么我建议您对我的请求进行修改https://unity.codeplex.com/workitem/12777

2 个答案:

答案 0 :(得分:2)

问题是,当AInjectionProperty注册时,会在Unity内部创建一个策略来解析B依赖关系。该策略与类型A的构建密钥相关联,并且类型为SpecifiedPropertiesSelectorPolicy(实现IPropertySelectorPolicy)。此策略存储InjectionProperty值(即new B())。

再次重新注册A时,政策未从政策列表中移除,因此当IA(最终A)被解析时,Unity会发现IPropertySelectorPolicy已关联使用类型A的构建键,并使用该值注入A

一种解决方案是删除IPropertySelectorPolicy。删除后,当A被解析时,Unity将创建一个新的策略来解析B依赖关系,这将返回在容器中注册的现有单例。

有了这个,单元测试应该通过:

[TestMethod]
public void UnityShouldCorrectlyResolveReRegisteredType()
{
    var container = new UnityContainer();

    container.RegisterType<IB, B>(new ContainerControlledLifetimeManager())
             .RegisterType<IA, A>(new ContainerControlledLifetimeManager(), 
                                  new InjectionProperty("B", new B()))


    container.RegisterType<IA, A>(new ContainerControlledLifetimeManager(),
                                  new ClearPropertySelectorPolicy());

    var a = container.Resolve<IA>();
    var b = container.Resolve<IB>();

    Assert.AreEqual(a.B, b);
}

public class ClearPropertySelectorPolicy : InjectionMember
{
    public override void AddPolicies(Type serviceType,
        Type implementationType,
        string name,
        Microsoft.Practices.ObjectBuilder2.IPolicyList policies)
    {
        policies.Clear<IPropertySelectorPolicy>(
            new NamedTypeBuildKey(implementationType, name));
    }
}

但是如果没有注入属性(可能是构造函数中的依赖项)会怎么样?在这种情况下,清除IPropertySelectorPolicy将不起作用;我们需要明确另一项政策(但要明确哪项政策?)。我认为以下内容清除了构建密钥的所有策略:

public class ClearAllPolicies : InjectionMember
{
    public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
    {
        var buildKey = new NamedTypeBuildKey(implementationType, name);

        policies.Clear<IBuildKeyMappingPolicy>(buildKey);
        policies.Clear<IConstructorSelectorPolicy>(buildKey);
        policies.Clear<IBuildPlanCreatorPolicy>(buildKey);
        policies.Clear<IBuildPlanPolicy>(buildKey);
        policies.Clear<IMethodSelectorPolicy>(buildKey);
        policies.Clear<IPropertySelectorPolicy>(buildKey);
        policies.Clear<ILifetimeFactoryPolicy>(buildKey);
        policies.Clear<ILifetimePolicy>(buildKey);
        policies.Clear<IBuilderPolicy>(buildKey);

        DependencyResolverTrackerPolicy.RemoveResolvers(policies, buildKey);
    }
}

一些警告:

    由于锁定,
  • policies.Clear()相当昂贵
  • 这是基于几分钟的分析,并且尚未经过测试(除了您的单元测试) - 场景可能会变得非常复杂。
  • 并发注册和解决可能会导致问题,因此,如果是这种情况,您可能需要自己管理同步。

另外,我不会真的推荐这种方法作为通用(非测试)方法。根据场景,可能有其他方法可以工作(例如命名注册,解析器覆盖,工厂等)。如果你“设置并忘记它”,Unity往往会发挥最佳效果。也就是说,在应用程序启动时,将执行所有注册,然后在应用程序对象的持续时间内解析而不修改注册。

答案 1 :(得分:1)

我查看了Unity的源代码,当传递给RegisterType的InjectionMembers数组的长度大于0时,似乎只删除了类型的现有BuildPlan。

if (injectionMembers.Length > 0)
{
    this.ClearExistingBuildPlan(to, name);
    foreach (InjectionMember injectionMember in injectionMembers)
      injectionMember.AddPolicies(from, to, name, (IPolicyList) this.policies);
}

针对您描述的问题,可能的解决方法是使用InjectionFactory作为第二个RegisterType调用的InjectionMember。

.RegisterType<IA, A>(new ContainerControlledLifetimeManager(), new InjectionFactory(c=>c.Resolve<IB>());