Search code examples
c#scheduled-taskssaganservicebus5

NServiceBus Saga has no IPersistTimeouts component registered


I have a self-hoted WCF Host (IIS) where I get this exception during the NServiceBus configuration:

Exception thrown: 'Autofac.Core.Registration.ComponentNotRegisteredException' in NServiceBus.Core.dll

Additional information: The requested service 'NServiceBus.Timeout.Core.IPersistTimeouts' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.

I am using NSB 5.2.9 with in-memory persistence and MSMQ transport.

This is my configuration for a self-hosted WCF host (IIS). No App.config! **This is called after Autofac registrations in global.asax:

    public static void CreateSelfHost( string endpointName, ILifetimeScope container )
    {
        if ( Bus != null )
            return;

        lock ( syncLock )
        {
            var config = new BusConfiguration();

            config.UseContainer<AutofacBuilder>( c => c.ExistingLifetimeScope( container ) );

            var includesBuilder = AllAssemblies.Matching( "Company.App." );
            config.AssembliesToScan( includesBuilder );

            config.UseSerialization<JsonSerializer>();
            config.UseTransport<MsmqTransport>();
            config.UsePersistence<InMemoryPersistence>();
            config.DisableFeature<SecondLevelRetries>();    //turn off for in-mem persistence, otherwise could lose messages
            config.EndpointName( endpointName );


            config.EnableInstallers(); //ensures msmq is created
            config.PurgeOnStartup( true ); //only for self-hosted

            config.Transactions().Disable();
            config.DisableFeature<StorageDrivenPublishing>();

            Bus = NServiceBus.Bus.CreateSendOnly( config ); //create SendOnlyBus here
        }
    }

The configuration on all endpoints is done only via IProvideConfiguration<>.

In the project's properties the NServiceBus.Lite profile is set but that makes no difference.

I am new to NSB and I can't explain why this is happening. I was looking for methods to enable to said IPersistsTimeouts, but any method I find is marked as obsolete and does not work.

EDIT #1: The feature dump as requested by @DavidBoike

------------- FEATURES ----------------
Name: CriticalErrorHandling
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: CustomIDataBus
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [DataBus]
Name: DataBus
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -No databus properties was found in available messages
Name: Encryptor
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -No encryption properties was found in available messages
Name: ErrorSubscribers
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: ForwarderFaultManager
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: InMemoryFaultManager
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: 
Name: InstallationSupport
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: CriticalTimeMonitoring
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: 
Name: Audit
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: AutoSubscribe
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: ApplySubscriptions
Name: MsmqSubscriptionPersistence
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: Scheduler
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: CustomSerialization
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -CustomSerialization not enable since serialization definition not detected.
Name: ForwardReceivedMessages
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -No forwarding address was defined in the unicastbus config
Name: RegisterHandlersInOrder
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: SLAMonitoring
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: 
Name: LicenseReminder
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: Outbox
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: 
Name: InMemoryGatewayPersistence
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [Gateway]
Name: InMemoryOutboxPersistence
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [Outbox]
Name: InMemorySagaPersistence
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [Sagas]
Name: InMemorySubscriptionPersistence
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [MessageDrivenSubscriptions]
Name: InMemoryTimeoutPersistence
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [TimeoutManager]
Name: TimeoutManagerBasedDeferral
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: UnicastBus
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: BinarySerialization
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -BinarySerialization not enable since serialization definition not detected.
Name: BsonSerialization
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -BsonSerialization not enable since serialization definition not detected.
Name: JsonSerialization
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: XmlSerialization
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -XmlSerialization not enable since serialization definition not detected.
Name: MsmqTransportConfigurator
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: TimeoutManager
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: [TimeoutManagerBasedDeferral]
Startup Tasks: None
Name: Sagas
Version: 5.2.9
Enabled by Default: Yes
Status: Disabled
Deactivation reason: Did not fulfill its Prerequisites:
   -No sagas was found in scanned types
Name: SecondLevelRetries
Version: 5.2.9
Enabled by Default: Yes
Status: Enabled
Dependencies: [ForwarderFaultManager]
Startup Tasks: None
Name: DataBusFileBased
Version: 5.2.9
Enabled by Default: No
Status: Disabled
Deactivation reason: Did not meet one of the dependencies: [DataBus]
Name: StorageDrivenPublishing
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: None
Startup Tasks: None
Name: MessageDrivenSubscriptions
Version: 5.2.9
Enabled by Default: No
Status: Enabled
Dependencies: None
Startup Tasks: None

EDIT #2:

After applying David's suggestions and keeping the configuration to a minimum, I still get an exception ONLY when using MsmqPersistence isntead of InMemoryPersistence. I have not tried other persistence storages.

This exception is thrown by an MVC application which has a full bus (not SendOnlyBus), and is configured as follows:

    public void ConfigureServiceBus( IContainer container, IAppBuilder app )
    {
        var busConfiguration = new BusConfiguration();

        busConfiguration.UseContainer<AutofacBuilder>( c => c.ExistingLifetimeScope( container ) );
        busConfiguration.EnableInstallers();

        var inc = AllAssemblies.Matching( "Company." )
                                            .And( "NServiceBus" )
                                            .And( "ServiceControl" );
        config.AssembliesToScan( inc );

        config.UsePersistence<MsmqPersistence>();
        config.UseSerialization<JsonSerializer>();
        config.UseTransport<MsmqTransport>();

        config.EndpointName( endpointName );

        var startableBus = NServiceBus.Bus.Create( busConfiguration );
        startableBus.Start();
    }

Exception thrown: 'System.NullReferenceException' in NServiceBus.Core.dll Additional information: Object reference not set to an instance of an object.

Call stack:

ServiceBus.Core.dll!NServiceBus.Timeout.Hosting.Windows.TimeoutPersisterReceiver.Poll(object obj) Line 90 C#

Autofac config in MVC:

public static IContainer ConfigureAutofac( IAppBuilder app )
{
    ContainerBuilder builder = new ContainerBuilder();

    // Register your MVC controllers.
    builder.RegisterControllers( typeof( MvcApplication ).Assembly );

    builder.RegisterType<...>().AsImplementedInterfaces();
    //...

    // Set the dependency resolver to be Autofac.
    IContainer container = builder.Build();

    var resolver = new Autofac.Integration.Mvc.AutofacDependencyResolver( container );

    DependencyResolver.SetResolver( resolver );

    return container;
}

Again, this is happening in an MVC application with MsmqPersistence, that subscribes to events published by the Some_Endpoint. The WCF host mentioned above initially sends commands to that Some_Endpoint.


Solution

  • There appear to be several things going on here, so I'll address a bunch of points that could contribute to the problem.

    Assembly Scanning

    This bit of code is controlling what assemblies get scanned for types important to NServiceBus:

    var includesBuilder = AllAssemblies.Matching( "Company.App." );
    config.AssembliesToScan( includesBuilder );
    

    This can cause weird things to happen if you happen to accidentally exclude important assemblies. Using AllAssemblies tries to add the NServiceBus assemblies, but for sure you are excluding any ServiceControl plugins.

    It's easier, if possible, to just omit this section and allow the endpoint to scan all the assemblies in the directory.

    Better would be:

    config.AssembliesToScan(AllAssemblies.Matching("Company.App.")
        .And("NServiceBus")
        .And("ServiceControl"));
    

    Or even better, don't whitelist, but use a blacklist if you absolutely must because you have some crazy assembly with thousands of unrelated types you don't want to bother scanning:

    config.AssembliesToScan(AllAssemblies.Except("CrazyAssemblies"));
    

    SendOnly

    In your WCF code snippet you are creating a send-only bus but you have some odd configuration choices above it:

    config.EnableInstallers(); //ensures msmq is created
    config.PurgeOnStartup( true ); //only for self-hosted
    
    config.Transactions().Disable();
    config.DisableFeature<StorageDrivenPublishing>();
    
    Bus = NServiceBus.Bus.CreateSendOnly( config ); //create SendOnlyBus here
    

    A send-only endpoint, by definition, does not receive messages, so it does not create queues. EnableInstallers() will not be needed, because no queues need to be set up. PurgeOnStartup(true) doesn't make much sense, since there's no queue to purge.

    Additionally, you're talking about sagas and timeout queues, but a send-only endpoint can't process messages, which means no sagas either. These combinations of things could be causing weird edge cases.

    Enabling/Disabling Features

    I see a few calls disabling certain features:

    config.DisableFeature<SecondLevelRetries>();    //turn off for in-mem persistence, otherwise could lose messages
    
    config.DisableFeature<StorageDrivenPublishing>();
    

    SecondLevelRetries is one of the things that requires timeouts, so if you disable that and have no sagas, I could see where there could be a unique combination of settings that would result in the InMemory timeout persister not getting registered, which would be a bug.

    Note that in your feature output:

    • InMemoryTimeoutPersistence is disabled due to TimeoutManager
    • Yet, TiemoutManager is enabled. This could possibly be a bug.

    You could always try to force the issue:

    config.EnableFeature<InMemoryTimeoutPersistence>();
    

    But it's usually better to allow the features to figure things out for themselves and not enable/disable them individually.

    Couple related notes:

    1. Disabling SecondLevelRetries in order to not lose messages is kind of backwards. With in-memory persistence, you WILL lose messages. It's only suitable for development. For production you should use one of the other persistence options. I can, however, see how it would be valuable to skip SLR in development in order to not have the exception traces repeat when debugging things.
    2. Why are you disabling StorageDrivenPublishing? MSMQ has no built-in Pub/Sub (like RabbitMQ or Azure Service Bus would) so without this feature you will not be able to publish events at all.

    Miscellaneous

    It could be related to how your Autofac container is configured before you pass it into NServiceBus. Hard to speculate since I can't see that code.

    I've tried to make a repro with the code you've provided and I've been unable to get it to fail. If none of the above ends up working and it's possible to make a simple repro (for example, a bus being created within a bare-bones Windows Console Application project) then it should be fairly easy to fix, or at the very least, make sure a better error message is provided.

    EDIT: MsmqPersistence

    It makes sense that you would get a NullReferenceException when using MsmqPersistence. MsmqPersistence ONLY includes subscription storage. It does not provide implementations of the other persistence types, including Timeouts, Sagas, Gateway, and Outbox. (I may have missed one?)

    In any case, MsmqPersistence is a relic. Don't use it!

    For development you can use in-memory. For other environments you should be using NHibernate or RavenDB.