Ninject和转发类型(在Castle Windsor中已知)

时间:2010-10-11 17:37:07

标签: ninject

在Castle Windsor中,有一项称为转发类型的功能,您可以在其中为多个服务配置一个组件。例如:

var container = new WindsorContainer();
container.Register(
   Component.For<Bar>().Forward<IFoo>()
      .ImplementedBy<FooBar>());
var foo = container.Resolve<IFoo>();
var bar = container.Resolve<Bar>();
Debug.Assert(foo == bar);

(这个Debug.Assert有效'因为默认将Windsor注册为单例)

如何在Ninject(版本2)中实现此目的?

2 个答案:

答案 0 :(得分:1)

我很想知道是否有其他人使用更好的解决方案,但这里有一个选项:

绑定时使用特殊的IProvider来执行前进:

public class ForwardProvider<ForwardType> : Ninject.Activation.IProvider
{
    #region IProvider Members
    public object Create( IContext context )
    {
        return context.Kernel.Get<ForwardType>();
    }

    public Type Type
    {
        get { return typeof( ForwardType ); }
    }
    #endregion
}

然后绑定时:

IKernel kernel = new StandardKernel();
kernel.Bind<ITestAdapter>().To<TestAdapter>().InSingletonScope();
kernel.Bind<IAnotherAdapter>().ToProvider<ForwardProvider<ITestAdapter>>();

和我的测试:

var foo = kernel.Get<ITestAdapter>();
foo.Indicator = 5;

var bar = kernel.Get<TestAdapter>();
Assert.That( foo.Indicator, Is.EqualTo( 5 ) );

var baz = kernel.Get<IAnotherAdapter>();
Assert.That( baz.Indicator, Is.EqualTo( 5 ) );

答案 1 :(得分:0)

我使用@dave thieben提供的解决方案的略微变化。或者更确切地说,根据具体情况,我使用两种不同的方法。

第一种方法与Dave基本相同,但没有创建单独的提供者。

var kernel = new StandardKernel();
kernel.Bind<FooBar>().ToSelf().InSingletonScope();
kernel.Bind<IFoo>().ToMethod(ctx => ctx.Kernel.Get<FooBar>());
kernel.Bind<IBar>().ToMethod(ctx => ctx.Kernel.Get<FooBar>());

这种方法的优点是我们可以获得通用约束的好处。但是,我们需要为每个转发类型显式创建绑定。

第二种方法更精细,涉及“劫持”流畅的界面。

var kernel = new StandardKernel();
kernel.Bind<FooBar>().ToSelf()
    .Forward().As<IFoo>().As<IBar>()
         .InSingletonScope();

为此,我们扩展了默认绑定构建器

public interface IBindingForwardSyntax<T> : IBindingWhenInNamedWithOrOnSyntax<T>
{
    IBindingForwardSyntax<T> As<TService>();
}

public class BindingForwardBuilder<T> 
    : BindingBuilder<T>, IBindingForwardSyntax<T>
{
    public BindingForwardBuilder(IBinding binding, IKernel kernel) 
        : base(binding, kernel) { }

    public IBindingForwardSyntax<T> As<TForwardedType>()
    {
        Kernel.Bind<TForwardedType>().ToMethod(ctx =>
        {
            var provider = Binding.GetProvider(ctx);
            if (!typeof(TForwardedType).IsAssignableFrom(provider.Type))
            {
                string message = string.Format(
                     "Invalid binding between '{0}' and '{1}'",
                         typeof(TForwardedType), typeof(T));
                throw new InvalidOperationException(message);
            }

            return (TForwardedType)ctx.Kernel.Get(typeof(T));
        });

        return this;
    }
}

最后我们添加一个扩展方法。

public static class BindingWhenInNamedWithOrOnSyntaxExtensions
{
    public static IBindingForwardSyntax<T> Forward<T>(
        this IBindingWhenInNamedWithOrOnSyntax<T> syntax)
    {
        return new BindingForwardBuilder<T>(syntax.Binding, syntax.Kernel);
    }
}

这样做的好处是我们只需要显式创建一个绑定。隐式创建转发类型的绑定。然而,这带来了一个巨大的缺点:我们没有得到通用约束的好处。因此,如果我们在定义绑定时不小心,它可能会在运行时失败。