Dependency injection in Masstransit routing slip Activity - masstransit

I working on a project using masstransit Courier. I am trying to inject some service dependencies into my Activities but this is not working. Can someone help how to use DI in MT activity. I could not find any working example with Activity implementation and DI
In my below code I am trying to inject IMyservice into my activity. But my activity constructor is never executing. Am I doing something wrong here?
public class DownloadImageActivity :
Activity<DownloadImageArguments, DownloadImageLog>
{
public DownloadImageActivity (IMyService service)
{
_service=service;
}
Task<ExecuteResult> Execute(ExecutionContext<DownloadImageArguments> context);
Task<CompensationResult> Compensate(CompensateContext<DownloadImageLog> context);
}
my service program code ActivityService.cs
_busControl = Bus.Factory.CreateUsingRabbitMq(x =>
{
IRabbitMqHost host = x.Host(new Uri(ConfigurationManager.AppSettings["RabbitMQHost"]), h =>
{
h.Username("guest");
h.Password("guest");
});
x.ReceiveEndpoint(host, ConfigurationManager.AppSettings["completepitaskqueue"], e =>
{
e.PrefetchCount = 100;
e.ExecuteActivityHost<CompletePiTaskActivity, ComplePiTaskArguments>(Program.Container);
// e.ExecuteActivityHost(
// DefaultConstructorExecuteActivityFactory<CompletePiTaskActivity, ComplePiTaskArguments>.ExecuteFactory, c => c.UseRetry(r => r.Immediate(5)));
});
My program.cs
public static IContainer Container;
static int Main(string[] args)
{
ConfigureLogger();
Container = builder.Build();
ActivityHelper.Container = Container;
// MassTransit to use Log4Net
Log4NetLogger.Use();
return (int)HostFactory.Run(x => x.Service<ActivityService>());

It depends upon which container you are using, some are more supported than others (out of the box, anyway, any should work).
For example, to configure a receive endpoint with an activity using Autofac, you would specify:
cfg.ReceiveEndpoint(host, "execute-activity", e =>
{
e.ExecuteActivityHost<TActivity, TArguments>(container);
});
Where container is either the builder context or the container. This will register the appropriate scope providers and activity factories for your activity.
This uses the extension method: https://github.com/MassTransit/MassTransit/blob/develop/src/Containers/MassTransit.AutofacIntegration/AutofacExtensions.cs#L300

Related

MassTransit failing to consume

I'm switching some code which uses MassTransit (v7.2.2 on .NET 5) to use a more declarative format (and away from multiple calls to ReceiveEndpoint()) and ideally to using ConsumerDefinitions for the configuration (though not part of this example for simplicity), along with some Dependency Injection with Quartz.NET (yanked from this example, though it running 3.3.3), in doing so I find now that my Consumers are not consuming, despite messages being sent and examples referenced. Take the following standing up of the MassTransit service:
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(mt =>
{
mt.AddBus(provider => Bus.Factory.CreateUsingInMemory(cfg =>
{
//cfg.AutoStart = true; //No change when on
cfg.UseInMemoryOutbox();
cfg.ConfigureEndpoints(provider);
}));
mt.AddConsumer<TheMessageConsumer>();
services.AddMediator(cfg =>
{
cfg.AddConsumer<TheMessageConsumer>();
});
});
services.AddMassTransitHostedService();
});
var host = hostBuilder.Build();
var busControl = host.Services.GetService<IBusControl>();
busControl.Start(); //Just in case
var message = new TheMessage() { Message = $"<Message-{DateTime.Now.ToLongTimeString()}>" };
Console.WriteLine($"Sending: {message.Message}");
await busControl.Publish(message);
host.Run();
For note, the breaking out of the message sending here is to simplify my repro, as in my full code base, it's being sent by a Quartz fired job.
For this example, the message & receiver are also quite simple:
public class TheMessage
{
public string Message { get; set; }
}
public class TheMessageConsumer : IConsumer<TheMessage>
{
public Task Consume(ConsumeContext<TheMessage> context)
{
Console.WriteLine($"Message received: {context.Message.Message}");
return Task.CompletedTask;
}
}
The bus is started, either in the case I explicitly start it, the AutoStart flag is set, or the MassTransitHostedService does it, yet the message doesn't get received. Ditto if I have the full example with Quartz firing off a job with messages much later.
Can someone suggest what I am missing?
The code you posted is seriously a hodgepodge of snippets, none of which make any sense when used together. For example, AddBus is deprecated, and mediator has no business being in that project at all.
I'd suggest using one of the MassTransit Templates to create a new project from scratch (you may need to up the NuGet versions to 7.2.2).
Watch this video which explains the templates and how to use them.
With your comment, and updated question, you really only need the following:
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(mt =>
{
mt.AddConsumer<TheMessageConsumer>();
mt.UsingInMemory((context,cfg) =>
{
cfg.UseInMemoryOutbox();
cfg.ConfigureEndpoints(context);
}));
});
services.AddMassTransitHostedService();
});
var host = hostBuilder.Build();
await host.RunAsync();
If you want to test it, and send messages in the same process, simply add a BackgroundService (after AddMassTransitHostedService) to publish your messages.
You shouldn't publish until after the bus has been started.

Masstransit unti tests problem and ISendEndpointProvider within DI

I want to unit test services which uses Masstransit(rmq) to send messages. According to the doc, there is InMemoryTestHarness class for this. But I can't figure out how can I use it in my scenario.
I use AbpBoilerplate framework, so first of all I need to register Masstransit within Castle Windsor. For test module I do it smth like this:
var harness = new InMemoryTestHarness();
harness.Start().Wait(TimeSpan.FromSeconds(1));//harness.Bus is not null now
IocManager.IocContainer.Register(Component.For<InMemoryTestHarness>().Instance(harness));
IocManager.IocContainer.Register(Component.For<IBus, IBusControl>().Instance(harness.Bus));
Service I want to test is following:
public class ProcessingService
{
private readonly ProjectService _projectService;
//NOTE previously there was IBus interface...
private readonly ISendEndpointProvider _provider;
public ProcessingService(ISendEndpointProvider provider, [NotNull] ProjectService projectService )
{
_ProjectService = ProjectService ?? throw new ArgumentNullException(nameof(ProjectService));
_provider = provider;
}
public async Task ProcessFileAsync([NotNull] fileInput FileInput,
[NotNull] IUserProcessingSettings userProcessingSettings)
{
if (fileInput == null) throw new ArgumentNullException(nameof(fileInput));
if (userProcessingSettings == null) throw new ArgumentNullException(nameof(userProcessingSettings));
var entity = await _projectService.GetSomeEntityAsync();
ProcessFileCommand msg = new ProcessFileCommand(entity);
await _provider.Send(msg).ConfigureAwait(false);
}
}
The first problem with code above is that for now if I use ISendEndpointProvider instead of IBus as suggeted in the doc it is simply cannot resolve dependecies, no ISendEndpointProvider registered. And I'm not quite understand how to handle it within InMemoryTestHarness abstraction...
Here is the test method and consumer class:
[Fact]
public async Task Simple_Masstranist_Consumer_Test1()
{
//it was injected and started in ABP test module
var harness = Resolve<InMemoryTestHarness>();
//NOTE which one is correct option -- handler or consumer?
var consumerHarness = harness.Consumer<MyConsumer>();
// await harness.SubscribeHandler<MyConsumer>().ConfigureAwait(false);
try
{
var processingService = Resolve<ProcessingService>();
var emptyFileImput = FileInput.EmptyFileInput(1);
await ProcessingService.ProcessFileAsync(emptyFileImput).ConfigureAwait(false);
//NOTE test hangs on this command
consumerHarness.Consumed.Select<ProcessFileCommand>().Any().ShouldBeTrue();
// the consumer publish the event
harness.Published.Select<ProcessFileCommand>().Any().ShouldBeTrue();
}
finally
{
//already started...
await harness.Stop().ConfigureAwait(false);
}
}
public class MyConsumer : IConsumer<ProcessFileCommand>
{
public Task Consume(ConsumeContext<ProcessFileCommand> context)
{
context.Message.ShouldNotBeNull();
context.Message.EntityId.ShouldBe(1);
context.Message.FilePath.ShouldBe("some_path");
return Task.FromResult(true);
}
}
My expectations for InMemoryTestHarness is that it is a wrapper of InMemory bus suitable for tests, so when my service send some message, I expect that it would be reflected in proper structures of InMemoryTestHarness. When I've used IBus instead of ISendEndpointProvider test code simply hang on
consumerHarness.Consumed.Select().Any().ShouldBeTrue(); line of code. It seems that it blocks for some event which will neven occur.
So, my questions are:
Is it correct to use InMemoryTestHarness to mock message queue in my scenario, maybe I should configure manually InMemoryBus for that?
2)If I want to use ISendEndpointProvider within DI, what should I register?
Thanks in advance
Update:
var testHarness = new InMemoryTestHarness();
IocManager.IocContainer.Register(Component.For<InMemoryTestHarness>().UsingFactoryMethod(kernel =>
{
var busRegistrationContext = kernel.Resolve<IRegistration>();
testHarness.OnConfigureInMemoryBus +=
configurator => configurator.ConfigureEndpoints(busRegistrationContext);
return testHarness;
}).LifestyleSingleton());
You can use the built-in container configuration with the test harness, I've included an example of how to do it with Castle Windsor below. This will ensure all required types are registered in the container, including IBus, ISendEndpointProvider, and IPublishEndpoint.
You do not need to start the bus, the harness starts and creates it for you.
Also, do not access IBusControl prior to resolving and starting the test harness, or the BusControl property on the harness will not be initialized, returning null.
[Test]
public async Task Should_startup_the_container_with_the_harness()
{
var container = new WindsorContainer()
.Register(Component.For<InMemoryTestHarness>().UsingFactoryMethod(kernel =>
{
var testHarness = new InMemoryTestHarness();
var busRegistrationContext = kernel.Resolve<IBusRegistrationContext>();
testHarness.OnConfigureInMemoryBus += configurator => configurator.ConfigureEndpoints(busRegistrationContext);
return testHarness;
}).LifestyleSingleton())
.AddMassTransit(x =>
{
x.AddConsumer<PingConsumer>();
x.AddBus(context => context.GetRequiredService<InMemoryTestHarness>().BusControl);
});
var harness = container.Resolve<InMemoryTestHarness>();
await harness.Start();
try
{
var bus = container.Resolve<IBus>();
await bus.Publish(new PingMessage());
Assert.That(await harness.Consumed.Any<PingMessage>());
}
finally
{
await harness.Stop();
container.Dispose();
}
}
This won't setup Consumer, Saga, or other harnesses, but it will configure endpoints on the test harness.

Application_End equivalent in ASP.NET Core rc2? [duplicate]

Is there a shutdown function when using Microsoft.AspNet.Server.Kestrel? ASP.NET Core (formerly ASP.NET vNext) clearly has a Startup sequence, but no mention of shutdown sequence and how to handle clean closure.
In ASP.NET Core you can register to the cancellation tokens provided by IApplicationLifetime
public class Startup
{
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStopping.Register(OnShutdown);
}
private void OnShutdown()
{
// Do your cleanup here
}
}
IApplicationLifetime is also exposing cancellation tokens for ApplicationStopped and ApplicationStarted as well as a StopApplication() method to stop the application.
For .NET Core 3.0+
From comments #Horkrine
For .NET Core 3.0+ it is recommended to use IHostApplicationLifetime instead, as IApplicationLifetime will be deprecated soon. The rest will still work as written above with the new service
In addition to the original answer, I had an error while trying to wire the IApplicationLifetime within the constructor.
I solved this by doing:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var applicationLifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(OnShutdown);
}
private void OnShutdown()
{
// Do your cleanup here
}
}
I solved it with the application lifetime callback events
Startup.cs
public void Configure(IHostApplicationLifetime appLifetime) {
appLifetime.ApplicationStarted.Register(() => {
Console.WriteLine("Press Ctrl+C to shut down.");
});
appLifetime.ApplicationStopped.Register(() => {
Console.WriteLine("Terminating application...");
System.Diagnostics.Process.GetCurrentProcess().Kill();
});
}
Program.cs
Also, use UseConsoleLifetime() while building the host.
Host.CreateDefaultBuilder(args).UseConsoleLifetime(opts => opts.SuppressStatusMessages = true);
This class is now obsolete, please refer to the new interface IHostApplicationLifetime. More info here.
When using .NET 6 without a Startup class, you can access the ApplicationStopping CancellationToken via the built web application in Program.cs, e.g.
var app = builder.Build();
...
app.Lifetime.ApplicationStopping.Register(() => ...);
...
app.Run();
In .Net 6 if you are not using Startup.cs. You could use ApplicationStopping method in Program.cs
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ContentRootPath = Directory.GetCurrentDirectory(),
WebRootPath = "wwwroot"
});
var app = builder.Build();
app.Lifetime.ApplicationStopping.Register(() =>
{
// Do Something
});
app.Run();

How can I attach a header to all published messages?

I have a particular header that I'd like to attach to all messages that I publish. I can attach this header on a case-by-case basis by specifying it in the Publish call:
_bus.Publish(myMessage, context => context.SetHeader("my key", "my value"));
This works fine but it becomes a chore to maintain this SetHeader call for every publish. Is there a way, during bus configuration or anywhere else, to specify a header that will be attached to all messages? That is, is there a way to do something like the following?
ServiceBusFactory.New(sbc =>
{
sbc.UseRabbitMq();
sbc.ReceiveFrom(hdoQueue);
// This is what I'd like to be able to do:
sbc.BeforePublish(context => context.SetHeader("my key", "my value"));
});
I believe there is a solution that involves implementing IOutboundMessageInterceptor but I can't find a way to attach my interceptor. There is a ServiceBusConfigurator.AddInboundInterceptor method but not a ServiceBusConfigurator.AddOutboundInterceptor method.
My intuition was correct, I was able to do what I wanted by implementing IOutboundMessageInterceptor:
public class AttachHeadersOutboundInterceptor : IOutboundMessageInterceptor
{
public void PreDispatch(ISendContext context)
{
context.SetHeader("my key", "my value");
}
public void PostDispatch(ISendContext context)
{
}
}
Oddly there is no ServiceBusConfigurator.AddOutboundInterceptor method, so I just created one (by copying the code for AddInboundInterceptor from github):
public static class MassTransitExtensions
{
public static void AddOutboundInterceptor(this ServiceBusConfigurator configurator,
IOutboundMessageInterceptor interceptor)
{
var builderConfigurator = new PostCreateBusBuilderConfigurator(bus =>
{
var interceptorConfigurator = new OutboundMessageInterceptorConfigurator(bus.OutboundPipeline);
interceptorConfigurator.Create(interceptor);
});
configurator.AddBusConfigurator(builderConfigurator);
}
}
And then I attach it during bus configuration:
ServiceBusFactory.New(sbc =>
{
sbc.UseRabbitMq();
sbc.ReceiveFrom(hdoQueue);
sbc.AddOutboundInterceptor(new AttachHeadersOutboundInterceptor());
});
Problem solved.

How To: Caliburn.Micro.Autofac and Windows Phone

Is there and example, tutorial or anything that shows how to use Caliburn.Micro.Autofac with Windows Phone?
I created a basic application with Caliburn.Micro only, and that runs fine. Then I decided to use Caliburn.Micro.Autofac, so I derived my Bootstrapper from Caliburn.Micro.Autofac.AutofacBootstrapper and called base.Configure() inside the Bootstrapper Configure() method. Now wen I ran the application I get "The type 'AppBootstrapper' was not found." exception.
Appreciate any help.
This is the bootstrapper I wrote for a WP7 project. It's based on Caliburn.Micro.Autofac.AutofacBootstrapper but fixes some bugs.
public class AppBootstrapper : PhoneBootstrapper
{
private IContainer container;
protected void ConfigureContainer(ContainerBuilder builder)
{
// put any custom bindings here
}
#region Standard Autofac/Caliburn.Micro Bootstrapper
protected override void Configure()
{
// configure container
var builder = new ContainerBuilder();
// register phone services
var caliburnAssembly = AssemblySource.Instance.Union(new[] { typeof(IStorageMechanism).Assembly }).ToArray();
// register IStorageMechanism implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageMechanism).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageMechanism>()
.SingleInstance();
// register IStorageHandler implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageHandler).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageHandler>()
.SingleInstance();
// The constructor of these services must be called
// to attach to the framework properly.
var phoneService = new PhoneApplicationServiceAdapter(RootFrame);
var navigationService = new FrameAdapter(RootFrame, false);
builder.Register<IPhoneContainer>(c => new AutofacPhoneContainer(c)).SingleInstance();
builder.RegisterInstance<INavigationService>(navigationService).SingleInstance();
builder.RegisterInstance<IPhoneService>(phoneService).SingleInstance();
builder.Register<IEventAggregator>(c => new EventAggregator()).SingleInstance();
builder.Register<IWindowManager>(c => new WindowManager()).SingleInstance();
builder.Register<IVibrateController>(c => new SystemVibrateController()).SingleInstance();
builder.Register<ISoundEffectPlayer>(c => new XnaSoundEffectPlayer()).SingleInstance();
builder.RegisterType<StorageCoordinator>().AsSelf().SingleInstance();
builder.RegisterType<TaskController>().AsSelf().SingleInstance();
// allow derived classes to add to the container
ConfigureContainer(builder);
// build the container
container = builder.Build();
// start services
container.Resolve<StorageCoordinator>().Start();
container.Resolve<TaskController>().Start();
// add custom conventions for the phone
AddCustomConventions();
}
protected override object GetInstance(Type service, string key)
{
object instance;
if (string.IsNullOrEmpty(key))
{
if (container.TryResolve(service, out instance))
return instance;
}
else
{
if (container.TryResolveNamed(key, service, out instance))
return instance;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.Resolve(typeof(IEnumerable<>).MakeGenericType(service)) as IEnumerable<object>;
}
protected override void BuildUp(object instance)
{
container.InjectProperties(instance);
}
private static void AddCustomConventions()
{
ConventionManager.AddElementConvention<Pivot>(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Pivot.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
ConventionManager.AddElementConvention<Panorama>(Panorama.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
}
#endregion
}
EDIT I have created a fork of Caliburn.Micro.Autofac and fixed the issue on GitHub. Hopefully the pull request will be accepted and this will become part of the main repository.
For now, you can access the bootstrapper, and AutofacPhoneContainer from here - https://github.com/distantcam/Caliburn.Micro.Autofac/tree/master/src/Caliburn.Micro.Autofac-WP7
I have implemented a proper version (in my opinion) of Caliburn.Micro.Autofac for Windows Phone. You can download it and test project from my blog. The blog post is in Russian but you'll find the link to ZIP file in the top of the post. The code is too big to post here, so please take from the blog. I've send this to David Buksbaum (the author of Caliburn.Micro.Autofac). Hope he will incorporate it into his code base soon.
UPDATE
What is fixed:
Components realizing IPhoneService and INavigationService services must be instantiated before registering in container.
Realized component implementing IPhoneContainer. Without it you can't use Autofac in Caliburn.Micro.

Resources