1

I am having problems getting NServiceBus 4.6.1 dependency injection working with Saga timeouts. I am using self-hosting in an ASP.NET web application and have property injection setup. It works when messages are sent from web controllers however, when a Timeout message is handled in the saga the same DI property is not being set and is null.

Here are the key bits of the setup:

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{


public static IWindsorContainer Container { get; private set; }

    protected void Application_Start()
    {
        ConfigureIoC();

        AreaRegistration.RegisterAllAreas();

        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        DeviceManagerDbInitializer.Instance.InitializeDatabase();

        ConfigureNServiceBus();
    }

    protected void Application_End()
    {
        if (Container != null)
        {
            Container.Dispose();
        }
    }


    private static void ConfigureIoC()
    {
        Container = new WindsorContainer()
            .Install(FromAssembly.This());

        var controllerFactory = new WindsorControllerFactory(Container.Kernel);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);

        GlobalConfiguration.Configuration.DependencyResolver
            = new WindsorDependencyResolver(Container);
    }


    private void ConfigureNServiceBus()
    {
        Configure.ScaleOut(s => s.UseSingleBrokerQueue());
        Configure.Instance.PeekInterval(500);
        Configure.Instance.MaximumWaitTimeWhenIdle(2000);
        Feature.Enable<TimeoutManager>();
        Feature.Enable<Sagas>();

        IStartableBus startableBus = Configure.With()
            .DefineEndpointName("MyQueue")
            .CastleWindsorBuilder(Container) //using NServiceBus CastleWindsor 4.6.1
            .UseTransport<AzureStorageQueue>()
            .UseAzureTimeoutPersister()
            .AzureSagaPersister()
            .PurgeOnStartup(false)
            .UnicastBus()
            .LoadMessageHandlers()
            .RunHandlersUnderIncomingPrincipal(false)
            .Log4Net(new DebugAppender { Threshold = Level.Warn })
            .RijndaelEncryptionService()
            .CreateBus();

        Configure.Instance.ForInstallationOn<Windows>().Install();

        startableBus.Start();
    }
}

Saga class

public class MySaga: Saga<MySagaData>,
    IAmStartedByMessages<StartMySagaCommand>,
    IHandleMessages<SomeMessage>,
    IHandleTimeouts<SomeTimeout>
{
    public DependentService MyInjectedService {get; set;}

    public override void ConfigureHowToFindSaga()
    {
        ConfigureMapping<StartMySagaCommand>( message => message.MyId).ToSaga( saga => saga.MyId );
        ConfigureMapping<SomeMessage>( message => message.MyId).ToSaga( saga => saga.MyId );
        ConfigureMapping<SomeTimeout>( message => message.MyId).ToSaga( saga => saga.MyId );
    }

    public void Handle(SomeMessage message)
    {
        // Here MyInjectedService is fine
        MyInjectedService.DoSomething(message);
    }

    public void Timeout(SomeTimeout state)
    {
        // Here MyInjectedService is always null
       MyInjectedService.DoSomething(state);
    }

}

I have tried solutions found here, here and here but none of them fixed the issue.

Community
  • 1
  • 1
Chris Morgan
  • 1,074
  • 11
  • 11
  • Hi Chris, It looks like you are missing the configure how to find saga? Also do you have the unique attribute in the saga data? – Sean Farmar Jun 27 '14 at 08:40
  • Sean, Thanks for the idea but I had just left the find code out of the example since the handlers were all being called correctly. The problem is that when the Timeout handler is invoked NServiceBus is not instantiating the MyInjectedService DI property like it does for the regular Handle method. – Chris Morgan Jun 30 '14 at 15:24
  • Any chance you can upload the code? or share it privately on dropbox? My email is s.farmar at gmail – Sean Farmar Jun 30 '14 at 18:42
  • Unfortunately this is part of a larger application that I can't share as is. I will need to create a stripped down version first. – Chris Morgan Jul 02 '14 at 21:41
  • I'd be happy to help if you can do that... – Sean Farmar Jul 03 '14 at 09:55
  • Sean, really appreciate the offers to help out with this issue. – Chris Morgan Jul 07 '14 at 19:05

1 Answers1

2

I figured out the problem here. Dependency injection was not working in the Saga's timeout handler because the Castle.Windsor lifestyle was set to LifestylePerWebRequest, e.g.:

public class WindsorServicesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register( Component.For<DependentService>()
                                         .LifestylePerWebRequest() );
    }
}

After changing the Lifestyle to LifeStyleTransient it started working. Any of the other 'non web request' lifestyles should work as well here.

In our setup the NServiceBus host is running under the web application and the regular message handlers were fine because they are being called in a controller action, e.g.:

[HttpPost]
public ActionResult DoSomething( int myId)
{
    _bus.Send( "MyBus", new SomeMessage { MyId = something.MyId } );
     return View();
}

When the saga handles the SomeMessage message for this it must still be part of the web request and Windsor resolves the dependency as normal. However, the timeouts are fired some time later (in this case five minutes) are they are not part of a web request. Windsor is not able to resolve the DependentService object and it stays null.

Chris Morgan
  • 1,074
  • 11
  • 11