2

Ninject 是否具有我可以用来装饰类或构造函数以让 Ninject 忽略它的属性?

我需要摆脱:

检测到两个服务的构造函数之间存在周期性依赖关系。

这是我的代码:

// Abstraction
public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

// Implementation
public class ShipOrderCommandHandler 
    : ICommandHandler<ShipOrderCommand>
{
    private readonly IRepository<Order> repository;

    public ShipOrderCommandHandler(
        IRepository<Order> repository)
    {
        this.repository = repository;
    }

    public void Handle(ShipOrderCommand command)
    {
        // do some useful stuf with the command and repository.
    }
}

我的通用装饰器:

public TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> decoratedHandler;

    public TransactionalCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler)
    {
        this.decoratedHandler = decoratedHandler;
    }

    public void Handle(TCommand command)
    {
        using (var scope = new TransactionScope())
        {
            this.decoratedHandler.Handle(command);
            scope.Complete();
        }
    }
}

我的容器注册:

kernel.Bind(typeof(ICommandHandler<>))
    .To(typeof(TransactionCommandHandlerDecora‌​tor<>));
4

2 回答 2

2

这是一个基于您的代码的工作示例。

public class AutoDecorationFacts
{
    readonly StandardKernel _kernel = new StandardKernel();

    public AutoDecorationFacts()
    {
        _kernel.Bind( typeof( ICommandHandler<> ) )
            .To( typeof( TransactionalCommandHandlerDecorator<> ) )
            .Named( "decorated" );
    }

    [Fact]
    public void RawBind()
    {
        _kernel.Bind( typeof( ICommandHandler<> ) ).To<ShipOrderCommandHandler>().WhenAnyAnchestorNamed( "decorated" );
        VerifyBoundRight();
    }

    void VerifyBoundRight()
    {
        var cmd = _kernel.Get<ICommandHandler<ShipOrderCommand>>();
        Assert.IsType<TransactionalCommandHandlerDecorator<ShipOrderCommand>>( cmd );
    }

最好用于Ninject.Extensions.Conventions:-

    [Fact]
    public void NameSpaceBasedConvention()
    {
        _kernel.Bind( scan => scan
            .FromThisAssembly()
            .SelectAllClasses()
            .InNamespaceOf<CommandHandlers.ShipOrderCommandHandler>()
            .BindAllInterfaces()
            .Configure( x => x.WhenAnyAnchestorNamed( "decorated" ) ) );
        VerifyBoundRight();
    }

    [Fact]
    public void UnconstrainedWorksTooButDontDoThat()
    {
        _kernel.Bind( scan => scan
            .FromThisAssembly()
            .SelectAllClasses()
            .BindAllInterfaces(  )
            .Configure( x=>x.WhenAnyAnchestorNamed("decorated" )));
        VerifyBoundRight();
    }
}

你的课:

公共类 ShipOrderCommand { }

// Abstraction
public interface ICommandHandler<TCommand>
{
    void Handle( TCommand command );
}

// Implementation
namespace CommandHandlers
{
    public class ShipOrderCommandHandler
        : ICommandHandler<ShipOrderCommand>
    {
        public ShipOrderCommandHandler(
            )
        {
        }

        public void Handle( ShipOrderCommand command )
        {
            // do some useful stuf with the command and repository.
        }
    }
}
public class TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> decoratedHandler;

    public TransactionalCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler )
    {
        this.decoratedHandler = decoratedHandler;
    }

    public void Handle( TCommand command )
    {
        this.decoratedHandler.Handle( command );
    }
}

(使用 Ninject 和 Ninject.Extensions.Conventions 的 NuGet 最新版本)

于 2012-04-05T14:12:48.873 回答
1

您应该能够对带有约束的原始接口使用上下文绑定Bind以使它们仅在正确的上下文中被考虑(即,当它们进入装饰器时)。

我有一个非常相似的When扩展,如果你还在看的话,我明天可以贴在这里。

编辑:我想到的代码(结果它并不想要你直接想要)

public static class NinjectWhenExtensions
{
    public static void WhenRootRequestIsFor<T>( this IBindingSyntax that )
    {
        that.BindingConfiguration.Condition = request => request.RootRequestIsFor<T>();
    }
}

public static class NinjectRequestExtensions
{
    public static bool RootRequestIsFor<T>( this IRequest request )
    {
#if false
        // Need to use ContextPreservingGet in factories and nested requests for this to work.
        // http://www.planetgeek.ch/2010/12/08/ninject-extension-contextpreservation-explained/
        return RootRequest( request ).Service == typeof( T );   
#else
        // Hack - check the template arg is the interface wer'e looking for rather than doing what the name of the method would actually suggest
        IRequest rootRequest = RootRequest( request );
        return rootRequest.Service.IsGenericType && rootRequest.Service.GetGenericArguments().Single() == typeof( T );
#endif
    }

    static IRequest RootRequest( IRequest request )
    {
        if ( request.ParentRequest == null )
            return request;

        return RootRequest( request.ParentRequest );
    }
}

用于附加装饰器:-

root.Bind<IEndpointSettings>().To<IAnonymousEndpointSettings>().WhenRootRequestIsFor<IAnonymousService>();
root.Bind<IEndpointSettings>().To<IAuthenticatedSettings>().WhenRootRequestIsFor<IServiceA>();

编辑 2:您应该能够使用创建一个When衍生物,它将 IX 的一般绑定排除在图片之外,除非它Resolve被输入到装饰器中。然后Bind,您的装饰器可以使用上下文保留(@Remo 有一篇文章)来确保进入上下文以便您的谓词可以决定,或者您可以将元数据添加到请求并让何时依赖它。

所以,我会: 1. 阅读 Context Preservation 2. 检查/转储符合您When条件的请求的上下文内容,并确定如何适当过滤。

(并希望有人能提供一个罐装的单班轮答案!)

上下文保留扩展可能会发挥作用。

于 2012-04-04T19:50:39.993 回答