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
Related
My main problem is to get the right instance of the SignalR hub.
Context: Im building a webapplication which communicates with a couple of external systems. CRUD operations in my application result in updating the databases of the external systems.
In this example i have 3 services running:
ExternalSystem | StateMachine | .NET CORE WebAPI
When i post the 'create employee' form, a RabbitMQ message will be sent from the WebAPI to the statemachine. The statemachine then sends a couple of create messages to my external system service which updates the database. Thereafter, it updates the statemachine to keep track of the createoperation.
Form -> API -> StateMachine -> ExternalSystem -> StateMachine -> API
So far so good. Now i would like to use SignalR to send the status updates to the client. So i've implemented this consumer in the API:
public class UpdatesConsumer :
IConsumer<IExternalSystemUpdateMessage>
{
private readonly IHubContext<UpdatesHub> _updaterHubContext;
public UpdatesConsumer(IHubContext<UpdatesHub> hubContext)
{
_updaterHubContext = hubContext;
}
public Task Consume(ConsumeContext<IExternalSystemUpdateMessage> context)
{
//return _updaterHubContext.Clients.Group(context.Message.CorrelationId.ToString()).SendAsync("SEND_UPDATE", context.Message.Message);// this.SendUpdate(context.Message.CorrelationId, context.Message.Message);
return _updaterHubContext.Clients.All.SendAsync("SEND_UPDATE", context.Message.Message);
}
}
This is my SignalR hub:
public class UpdatesHub :
Hub
{
public Task SendUpdate(Guid correlationId, string message)
{
return Clients.Group(correlationId.ToString()).SendAsync("SEND_UPDATE", message);
}
}
And this is how the Bus and consumer is instantiated:
public void ConfigureServices(IServiceCollection services)
{
_services = services;
services.AddMvc();
services.AddSignalR();
//services.AddSingleton<IHubContext<UpdatesHub>>();
WebAPI.CreateBus();
}
public static IServiceCollection _services;
static IBusControl _busControl;
public static IBusControl Bus
{
get
{
return _busControl;
}
}
public static void CreateBus()
{
IRMQConnection rmqSettings = Config.GetRMQConnectionConfig("rmq-settings.json", "connection");
_busControl = MassTransit.Bus.Factory.CreateUsingRabbitMq(x =>
{
var host = x.Host(BusInitializer.GetUri("", rmqSettings), h =>
{
h.Username(rmqSettings.UserName);
h.Password(rmqSettings.Password);
});
x.ReceiveEndpoint(host, "externalsystems.update",
e => { e.Consumer(() => new UpdatesConsumer((IHubContext<UpdatesHub>)Startup.__serviceProvider.GetService(typeof(IHubContext<UpdatesHub>)))); });
});
TaskUtil.Await(() => _busControl.StartAsync());
}
=========================================================================
So the problem is that _updaterHubContext.Clients in my Consumer class, always turn out to be empty. I've tested accessing the hub in a controller, and the clients do show up:
public class TestController : Controller
{
private readonly IHubContext<UpdatesHub> _hubContext;
public TestController(IHubContext<UpdatesHub> hubContext)
{
_hubContext = hubContext;
}
[HttpGet]
[Route("api/Test/")]
public IActionResult Index()
{
return View();
}
}
How can i get the right instance of the hub in my Consumer class? Or how can i access the IServiceCollection that .net is using?
Thnx in advance!
You can register your consumer so that MassTransit will resolve it from the IServiceProvider using the support provided in the MassTransit.Extensions.DependencyInjection package.
x.ReceiveEndpoint(host, "externalsystems.update", e =>
{
e.Consumer<UpdatesConsumer>(_serviceProvider);
});
Be sure to register your UpdatesConsumer in the container as well. This should resolve a new instance of the consumer for each message received on the endpoint.
Why not register Bus using Microsoft Dependency Injection. It should fix your issue, it will Resolve your consumer using IServiceProvider
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.
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();
}
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.
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.