Injecting NServiceBus using structuremap without global.asax - asp.net-mvc-4

I am new to NServiceBus and struggling to inject IBus in my controller using structure map. Actually after doing a little research i found that we can inject it by putting below code in Application_Start event of global.asax -
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Configure.With()
.Log4Net()
.StructureMapBuilder()
.XmlSerializer()
.MsmqTransport()
.IsTransactional(false)
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus();
}
and in my controller I can use IBus as property or constructor injection:
private IBus Bus { get; set; }
private ITest Test { get; set; }
public MyLinkController(IBus bus, ITest test)
{
Bus = bus;
Test = test;
}
This works fine and inject bus without any problem. But my problem is I do not have control over global.asax, so i want to put the configuration somewhere else e.g. i tried putting it in structuremap registry like below:
For<IBus>().Use(
() =>
NServiceBus.Configure.With()
.Log4Net()
.StructureMapBuilder()
.XmlSerializer()
.IsTransactional(true)
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus()
.Start()
);
But it doesn't help. Looks like structure map needs to know the configuration before its own initialization.
So is there any way to do it without application_start event?

I was able to do it through structure-map itself. This is how I changed my structure-map registry:
public ServiceBusRegistry()
{
ForSingletonOf<IBus>().Use(
NServiceBus.Configure.With()
.Log4Net()
.DefaultBuilder()
.XmlSerializer()
.MsmqTransport()
.IsTransactional(false)
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus()
.Start()
);
}
After putting this in Registry class of structure map, I am able to inject IBus in my controller:
public MyController(IRepository<Test, int> repository, IBus bus)
{
_repository = repository;
_bus = bus;
}
Notice, here I used DefaultBuilder instead of StructureMapBuilder. I may try to improve it in further in future, But as of now its working fine for me.
UPDATED (after our project design changed):
Now we have a startup class in our API project(the project is ASP.NET web API) though we dont use global.asax's Application_start, we are able to put the startup code in this startup class:
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(WebApiPreStartup), "Start", Order = -999)] //make sure we are first
namespace ADP.DS.FrontOffice.Quoting.WebApi
{
public static class WebApiPreStartup
{
public static void Start()
{
GlobalFilterConfig.Configure(GlobalConfiguration.Configuration);
BusConfig.Init();
}
}
}
and the configuration is in separate class:
public static void Init()
{
Configure.Serialization.SystemXml();
Configure.ScaleOut(s => s.UseSingleBrokerQueue());
var configUnicastBus = Configure.With()
.StructureMapBuilder(ObjectFactory.Container)
.UseTransport<NServiceBus.RabbitMQ>()
.PurgeOnStartup(false)
.UnicastBus();
configUnicastBus.SendOnly();
}

Related

NServiceBus Dependency Injection not working for Saga Timeouts

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.
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.

NService Bus : "Unable to set the value for key: ScaleOut.UseSingleBrokerQueue."

I got this type of error when using nservicebus.structuremap. This is my code.
EndPointConfig.cs
namespace NSBus.Server
{
using NServiceBus;
/*
This class configures this endpoint as a Server. More information about how to configure the NServiceBus host
can be found here: http://particular.net/articles/the-nservicebus-host
*/
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, UsingTransport<Msmq>, IWantCustomInitialization
{
public static IBus Bus { get; private set; }
public void Init()
{
ConfigureIocTool();
}
private static void ConfigureIocTool()
{
var container = new Container(y => y.Scan(scan =>
{
scan.TheCallingAssembly();
scan.AssemblyContainingType<SanelibRegistry>();
scan.AssemblyContainingType<CommonRegistry>();
scan.AssemblyContainingType<CoreRegistry>();
scan.WithDefaultConventions();
scan.LookForRegistries();
}));
Bus = Configure.With()
.StructureMapBuilder(container)
.MsmqSubscriptionStorage()
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus()
.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
}
}
this code running successfully but i got error after some time.
Since I am using NServiceBus.Host, i don't need to create the bus in your endpoint config:
my initialization becomes something like this:
Since the AsA_Server role is beign used, it already will set the purge queue on startup to false, use unicast bus, etc.
The bus will be created and will be available via DI in all the message handlers.
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, UsingTransport<Msmq>, IWantCustomInitialization
{
public void Init()
{
var container = new Container(y => y.Scan(scan =>
{
scan.TheCallingAssembly();
scan.AssemblyContainingType<SanelibRegistry>();
scan.AssemblyContainingType<CommonRegistry>();
scan.AssemblyContainingType<CoreRegistry>();
scan.WithDefaultConventions();
scan.LookForRegistries();
}));
Configure.With()
.StructureMapBuilder(container)
.MsmqSubscriptionStorage();
}
}
For more details see:
http://particular.net/articles/the-nservicebus-host (section built-in configurations) and also
http://particular.net/articles/containers
Also, for subscription storage, either RavenDB or NHibernate (sql storage) is recommended for production and not msmq.
Hope this helps,
Nikunj Balar

No message serializer has been configured

Upgrading from version 3 of nservicebus to version 4, and receiving the following error message
"No message serializer has been configured."
stack trace:
at NServiceBus.Unicast.UnicastBus.ValidateConfiguration() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Unicast\UnicastBus.cs:line 866
at NServiceBus.Unicast.UnicastBus.Start(Action startupAction) in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Unicast\UnicastBus.cs:line 739
at NServiceBus.Unicast.UnicastBus.Start() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Unicast\UnicastBus.cs:line 718
at CycleMonkey.Inventory.CreateOrder.IT_OPS.CustomInit.Init() in d:\dev\backup\soa_cyclemonkey\Inventory\Inventory.CreateOrder\IT_OPS\CustomInit.cs:line 20
at NServiceBus.Hosting.Configuration.ConfigManager.ConfigureCustomInitAndStartup() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Hosting\Configuration\ConfigurationManager.cs:line 43
at NServiceBus.Hosting.GenericHost.PerformConfiguration() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Hosting\GenericHost.cs:line 126
at NServiceBus.Hosting.GenericHost.Start() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\NServiceBus.Core\Hosting\GenericHost.cs:line 29
at NServiceBus.Hosting.Windows.WindowsHost.Start() in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\hosting\NServiceBus.Hosting.Windows\WindowsHost.cs:line 56
at NServiceBus.Hosting.Windows.Program.<>c_DisplayClassd.b_5(WindowsHost service) in c:\TeamCity\buildAgent\work\d4de8921a0aabf04\src\hosting\NServiceBus.Hosting.Windows\Program.cs:line 76
at Topshelf.Internal.ControllerDelegates1.StartActionObject(Object obj) in c:\Projects\TopShelfForNSB\src\Topshelf\Internal\ControllerDelegates.cs:line 18
at Topshelf.Internal.IsolatedServiceControllerWrapper1.<>c_DisplayClass2.b_1(TService service) in c:\Projects\TopShelfForNSB\src\Topshelf\Internal\IsolatedServiceControllerWrapper.cs:line 65
at Topshelf.Internal.ServiceController1.<.cctor>b__1(ServiceController1 sc) in c:\Projects\TopShelfForNSB\src\Topshelf\Internal\ServiceController.cs:line 35
at Magnum.StateMachine.LambdaAction1.Execute(T instance, Event event, Object parameter) in :line 0
at Magnum.StateMachine.EventActionList1.Execute(T stateMachine, Event event, Object parameter) in :line 0
Has something been missed in the upgrade? Version 3 of code that was working :
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server
{
}
public class CustomInit : IWantCustomInitialization
{
public void Init()
{
Configure.Instance
.CastleWindsorBuilder()
.DefaultBuilder()
.Sagas()
.RunTimeoutManagerWithInMemoryPersistence()
.ConfigureMongoSagaPersister<CreateOrderSagaData>("mongodb://localhost/create-order");
Configure.Instance
.XmlSerializer()
.MsmqSubscriptionStorage()
.MsmqTransport()
.UnicastBus();
}
}
Version 4 of the same code with the suggested changes required with the upgrade
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, UsingTransport<Msmq>
{
}
public class CustomInit : IWantCustomInitialization
{
public void Init()
{
Configure.Features.Enable<Sagas>();
Configure.Serialization.Xml();
Configure.Instance
.CastleWindsorBuilder()
.UseInMemoryTimeoutPersister()
.ConfigureMongoSagaPersister<CreateOrderSagaData>("mongodb://localhost/create-order");
Configure.Instance
.MsmqSubscriptionStorage()
.UnicastBus()
.CreateBus()
.Start();
}
}
When bootstrapping a different container, use the IWantCustomInitialization interface together with the IConfigureThisEndpoint like #JohnSimons mentioned.
Also, when you are implementing the IWantCustomInitialization in the IConfigureThisEndpoint, there is no bus yet, so an instance has not been created at this point, so you'd need to use Configure.With() instead of Configure.Instance.
NOTE: You don't need to specify UsingTransport as Msmq is the default transport.
You also don't need to specify Configure.Serialization.Xml() as Xml is the default serializer.
So, if you change your code to something like below, it should work:
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
{
public void Init()
{
Configure.Features.Enable<Sagas>();
Configure.With()
.CastleWindsorBuilder()
.UseInMemoryTimeoutPersister()
.MsmqSubscriptionStorage();
}
}

NServicebus DataBus

I need to move some large files, and I wanted to use NserviceBus as the files are not persisted on the system. I have been trying to get this up and running using the new databus feature in v.3.
The files are generated in one process, then passed onto the bus. My problem is that I can't get the bus started. I have a sample application below that basically mirrors my set up, except that in the 'real' application, the bus lives in a different application (not pushed to a server yet so I just run a dev instance)
#region Declarations
private IBus Bus { get; set; }
#endregion
#region Constructors
public Sender()
{
if (Directory.Exists(BasePath))
{
Bus = Configure.Instance
.FileShareDataBus(BasePath)
.UnicastBus().DoNotAutoSubscribe()
.CreateBus()
.Start();
}
}
#endregion
#region Methods
public void SendMessage()
{
// get a list of file names
List<string> fileList = Directory.GetFiles(#"c:\test\").ToList();
Bus.Send<GenericListMessage>(message =>
{
message.Name = "TEST";
message.FileList = new DataBusProperty<List<string>>(fileList);
});
}
#endregion
My EndpointConfig.cs looks like this:
public class EndpointConfig : IConfigureThisEndpoint, AsA_Client
{
}
internal class SetupDataBus : IWantCustomInitialization
{
public static string BasePath = #"c:\storage\";
public void Init()
{
Configure.Instance
.FileShareDataBus(BasePath)
.UnicastBus()
.DoNotAutoSubscribe();
}
}
I have changed my code in the Sender class to send a non-Databus message, and a simple message, and this worked fine. But when I try to use it to send a Databus message I keep getting a NullReferenceException for the bus. If anyone can give me a pointer I would really appreciate it.
You need to change
private IBus Bus { get; set; }
to:
public IBus Bus { get; set; }
To get dependecy injection for the bus.

NServiceBus: Object reference exception from NServiceBus.Host

I am getting an exception when I try to start a modified version of the PubSub sample. I am trying to do a few things like making subscription not automatic, and injecting my own ISubscriptionStorage implementation. Here is what I did to MyPublisher\EndpointConfig.cs:
using NServiceBus;
using NServiceBus.Grid.MessageHandlers;
using NServiceBus.ObjectBuilder;
using NServiceBus.Sagas.Impl;
namespace MyPublisher
{
class EndpointConfig : IConfigureThisEndpoint, IWantCustomInitialization
{
public void Init()
{
NServiceBus.Configure.With()
.DefaultBuilder()
.XmlSerializer()
.UnicastBus()
.LoadMessageHandlers(First<GridInterceptingMessageHandler>.Then<SagaMessageHandler>())
.DoNotAutoSubscribe();
NServiceBus.Configure.Instance.Configurer.ConfigureComponent<StreamSubscriptionStorage>(ComponentCallModelEnum.Singleton);
}
}
}
It used to be this:
using NServiceBus;
using NServiceBus.Grid.MessageHandlers;
using NServiceBus.Sagas.Impl;
namespace MyPublisher
{
class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher,
ISpecifyMessageHandlerOrdering
{
public void SpecifyOrder(Order order)
{
order.Specify(First<GridInterceptingMessageHandler>.Then<SagaMessageHandler>());
}
}
}
You forgot to include the .MsmqTransport() in your custom initialization.