如果对于具有不同绑定组件的相同组件有两个注册,它们应该接收不同的实例?

时间:2013-05-02 08:00:55

标签: c# castle-windsor

我试图了解Castle.Windsor的生活方式是如何运作的,我想我还没有得到它:)

因此,在下面的测试中,我按照以下假设进行测试:
1)如果组件绑定到某个东西,它应该在整个依赖关系层次结构中接收相同的事物实例,这是在文档中编写的,并且是可以理解的并且看起来很有效。

2)如果组件具有多个注册并且第一次注册被绑定,则对于绑定实例,它应该如1)中那样工作,对于其他实例,它应该生成非绑定注册中描述的实例。文档中没有提到这一点,但我看起来也很符合逻辑。

3)语法允许链接生活方式,因此通常在单个名称下我应该能够注册具有多个绑定组件的组件。它不在下面的测试中,但那是初始版本,并没有像我想象的那样工作。

4)(实际问题)如果对于具有不同绑定组件的相同组件有两个注册,它们应该接收不同的实例?我在这里看到了这个配置的两个问题:

a)Windsor不能解决所有依赖关系(bound2.DataContext为空)
b)bound.DataContext等于bound2.Service.DataContext我认为不正确(直观)。

[TestFixture]
public class IocTests
{      
    [Test]
    public void BoundRegistrationsTest()
    {
        var container = new WindsorContainer();
        container.Register(
            Component.For<IDataContext>()
                 .ImplementedBy<DataContext>()
                 .LifestyleBoundTo<BoundContextUser()
                 .Named("IDataContext_BoundContextUser"),
            Component.For<IDataContext>()
                 .ImplementedBy<DataContext>()
                 .LifestyleBoundTo<BoundContextUser2()
                 .Named("IDataContext_BoundContextUser2"),
            Component.For<IDataContext>()
                 .ImplementedBy<DataContext>()
                 .LifestyleTransient(),
            Component.For<BoundContextUser>()
                 .LifestyleTransient(),
            Component.For<BoundContextUser2>()
                 .LifestyleTransient(),
            Component.For<IService>()
                 .ImplementedBy<Service>()
                 .LifeStyleTransient(),
            Component.For<UnboudContextUser>()
                 .LifestyleTransient()
            );

        var bound = container.Resolve<BoundContextUser>();
        var bound2 = container.Resolve<BoundContextUser2>();
        var unbound = container.Resolve<UnboudContextUser>();

        Assert.AreEqual(bound.DataContext, bound.Service.DataContext);
        Assert.AreNotEqual(unbound.DataContext, unbound.Service.DataContext);

        // this fails
        Assert.AreEqual(bound2.DataContext, bound2.Service.DataContext);

        // if bound2.DataContext would not be null, this would fail too
        Assert.AreNotEqual(bound.DataContext, bound2.DataContext);
    }
}

public class BoundContextUser
{
    public IDataContext DataContext { get; set; }
    public IService Service { get; set; }
}

public class BoundContextUser2
{
    public IDataContext DataContext { get; set; }
    public IService Service { get; set; }
}

public interface IService
{
    IDataContext DataContext { get; set; }
}

public class Service : IService
{
    public IDataContext DataContext { get; set; }
}

public class UnboudContextUser
{
    public IDataContext DataContext { get; set; }
    public IService Service { get; set; }        
}

public class DataContext : IDataContext
{

}

public interface IDataContext
{

}

更新: 正如Marwijn正确地注意到的那样,我已经忘记了将生活方式设置为服务,因此4 b)不再是实际的,但仍然没有解决所有属性,如4 a)中所述。

1 个答案:

答案 0 :(得分:1)

第一个问题4b:

由于Service是单例,因此应用程序中只有1。所以bound.Service == bound2.service总是如此。第一次解析此服务时,它将在绑定的上下文中解析,因此它将获得绑定的Datacontext。 我认为这是一个配置错误,因为当绑定的生命周期结束时,在单例服务的生命周期之前,服务仍将保持对一个对象的引用,这个对象从容器的角度来看已经不再存在(Windsor将在它上面调用Dispose)实现IDisposable。

关于其他问题:您似乎将组件与类/接口混淆。每个Component.For语句都注册1个组件。使用特定接口注册的第一个组件被视为该接口的默认组件(除非被IsDefault覆盖)。当一个组件被解析时,它将为每个依赖项尝试创建/获取默认组件。如果无法解析此组件(因为它绑定到不在分辨率堆栈中的某些内容),它将停止。

如果要指定组件应尝试解析特定的其他组件,可以使用DependsOn指定应解析的组件。你可以看到下面的代码。

亲切的问候, Marwijn。

   var container = new WindsorContainer();
    container.Register(
        Component.For<IDataContext>().ImplementedBy<DataContext>(), // default datacontext
        Component.For<IService>().ImplementedBy<Service>(), // default service.

        Component.For<IDataContext>()
             .ImplementedBy<DataContext>()
             .LifestyleBoundTo<BoundContextUser>()
             .Named("IDataContext_BoundContextUser"),                 
        Component.For<IDataContext>()
             .ImplementedBy<DataContext>()
             .LifestyleBoundTo<BoundContextUser2>()
             .Named("IDataContext_BoundContextUser2"),
        Component.For<BoundContextUser>()
             .LifestyleTransient()
             .DependsOn(
                Dependency.OnComponent(typeof(IDataContext),"IDataContext_BoundContextUser"),
                Dependency.OnComponent(typeof(IService), "IService_BoundContextUser")),
        Component.For<BoundContextUser2>()
             .DependsOn(
                Dependency.OnComponent(typeof(IDataContext), "IDataContext_BoundContextUser2"),
                Dependency.OnComponent(typeof(IService), "IService_BoundContextUser2"))
             .LifestyleTransient(),

        Component.For<IService>()
             .ImplementedBy<Service>()
             .DependsOn(Dependency.OnComponent(typeof(IDataContext),"IDataContext_BoundContextUser"))
             .LifestyleBoundTo<BoundContextUser>()
             .Named("IService_BoundContextUser"),
        Component.For<IService>()
             .ImplementedBy<Service>()
             .DependsOn(Dependency.OnComponent(typeof(IDataContext), "IDataContext_BoundContextUser2"))
             .LifestyleBoundTo<BoundContextUser2>()
             .Named("IService_BoundContextUser2"),

        Component.For<UnboudContextUser>()
             .LifestyleTransient()
        );

    var bound = container.Resolve<BoundContextUser>();
    var bound2 = container.Resolve<BoundContextUser2>();
    var unbound = container.Resolve<UnboudContextUser>();

    Assert.AreEqual(bound.DataContext, bound.Service.DataContext);
    Assert.AreEqual(unbound.DataContext, unbound.Service.DataContext);
    Assert.AreEqual(bound2.DataContext, bound2.Service.DataContext);
    Assert.AreNotEqual(bound.DataContext, bound2.DataContext);