I am trying to receive an event from the RabbitMQ broker but something wents wrong, the Consume method of my consumer is never called, although the message is visible on the bus. Here's my IntegrationEvent class:
public abstract class IntegrationEvent
{
protected IntegrationEvent(Guid entityId,
string eventType)
{
EntityId = entityId;
EventType = eventType;
}
public Guid Id { get; } = Guid.NewGuid();
public DateTime CreatedAtUtc { get; } = DateTime.UtcNow;
public Guid EntityId { get; }
public string EventType { get; }
public DateTime? PublishedAtUtc { get; set; }
}
And the example inheritor:
public sealed class UserCreatedIntegrationEvent : IntegrationEvent
{
public UserCreatedIntegrationEvent(Guid id,
string login,
string firstName,
string lastName,
string mailAddress)
: base(id,
nameof(UserCreatedIntegrationEvent))
{
Login = login;
FirstName = firstName;
LastName = lastName;
MailAddress = mailAddress;
}
public string Login { get; }
public string FirstName { get; }
public string LastName { get; }
public string MailAddress { get; }
}
Publication logic:
public async Task PublishAsync(params IntegrationEvent[] events)
{
var globalPublicationTasks = events
.Select(#event =>
{
#event.PublishedAtUtc = DateTime.UtcNow;
return _publishEndpoint.Publish(#event);
});
await Task.WhenAll(globalPublicationTasks);
}
Receiver classes and the dependencies registry code:
public sealed class IntegrationEventListener : BackgroundService
{
public IntegrationEventListener(IBusControl busControl,
IServiceProvider serviceProvider,
IOptions<RabbitMQSettings> busConfiguration)
: base(busControl,
serviceProvider,
busConfiguration,
NullLogger.Instance)
{
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var handler = BusControl
.ConnectReceiveEndpoint(BusConfiguration.HostName, receiveEndpointConfigurator =>
{
receiveEndpointConfigurator
.Consumer<IntegrationEventTransmitter>(ServiceProvider);
});
await handler.Ready;
}
catch (Exception e)
{
...
}
}
}
public sealed class IntegrationEventTransmitter : IntegrationEventHandler<IntegrationEvent>
{
public override async Task HandleAsync(IntegrationEvent #event)
{
throw new System.NotImplementedException();
}
}
public abstract class IntegrationEventHandler<TIntegrationEvent>
: IIntegrationEventHandler<TIntegrationEvent>,
IConsumer<TIntegrationEvent>
where TIntegrationEvent : IntegrationEvent
{
public async Task Consume(ConsumeContext<TIntegrationEvent> context) =>
await HandleAsync(context.Message);
public abstract Task HandleAsync(TIntegrationEvent #event);
}
...
.AddRabbitMQ(configuration,
ExchangeType.Fanout,
true)
.AddScoped<IntegrationEventTransmitter>()
.AddHostedService<IntegrationEventListener>();
...
internal static IServiceCollection RegisterRabbitMQDependencies(
this IServiceCollection services,
IConfiguration configuration,
string exchangeType)
{
var rabbitMQSettings = configuration
.GetSection(RabbitMQSettingsSectionKey)
.Get<RabbitMQSettings>();
services
.AddMassTransit(configurator =>
{
configurator.AddConsumers(typeof(IntegrationEventHandler<IntegrationEvent>).Assembly);
})
.AddSingleton(serviceProvider => MassTransit.Bus.Factory.CreateUsingRabbitMq(configurator =>
{
configurator
.Host(rabbitMQSettings.HostName,
rabbitMQSettings.VirtualHostName,
hostConfigurator =>
{
hostConfigurator.Username(rabbitMQSettings.UserName);
hostConfigurator.Password(rabbitMQSettings.Password);
});
configurator.ExchangeType = exchangeType;
}))
.AddSingleton<IPublishEndpoint>(provider => provider.GetRequiredService<IBusControl>())
.AddSingleton<ISendEndpointProvider>(provider => provider.GetRequiredService<IBusControl>())
.AddSingleton<IBus>(provider => provider.GetRequiredService<IBusControl>())
.Configure<RabbitMQSettings>(configuration.GetSection(RabbitMQSettingsSectionKey));
return services;
}
In the RabbitMQ management panel i can notice that message is being properly published on the bus, the consumer is also connected to the broker but for some reason it does not consume the message. What am i doing wrong?
You should not connect a receiving endpoint, as it's completely unnecessary in this case. As Chris mentioned, configuring MassTransit for ASP.NET Core is properly described in the documentation, and it makes total sense to follow the documentation to avoid unnecessary complexity.
In your particular case, you don't start the bus, although it's even mentioned in the Common Mistakes article as the first thing.
Just do the following:
Use AddMassTransit in Startup and configure the receive endpoint normally
Add the handler directly there, or use a consumer class instead. It does not need to be a background service, MassTransit will call it when it receives a message
Register the MassTransit host by calling AddMassTransitHostedService
Related
I am trying to figure why "Sending" a message does not invoke state machine, but if I "Publish" a message, it works and I can see the state changing.
Following is my code, it is similar to the documentation, except that I am trying to "Send" a message.
Components
State Machine:
public class OrderState: SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public int CurrentState { get; set; }
public DateTime? OrderDate { get; set; }
}
public class OrderStateMachine: MassTransitStateMachine<OrderState>
{
public State Submitted { get; private set; }
public State Accepted { get; private set; }
public State Completed { get; private set; }
public Event<SubmitOrder> SubmitOrder { get; private set; }
public Event<OrderAccepted> OrderAccepted { get; private set; }
public Event<OrderCompleted> OrderCompleted { get; private set; }
public OrderStateMachine()
{
InstanceState(x => x.CurrentState, Submitted, Accepted, Completed);
Event(() => SubmitOrder, x => x.CorrelateById(context => context.Message.OrderId));
Event(() => OrderAccepted, x => x.CorrelateById(context => context.Message.OrderId));
Event(() => OrderCompleted, x => x.CorrelateById(context => context.Message.OrderId));
Initially(
When(SubmitOrder)
.Then(context => context.Instance.OrderDate = context.Data.OrderDate)
.TransitionTo(Submitted));
During(Submitted,
When(OrderAccepted)
.TransitionTo(Accepted));
During(Accepted,
Ignore(SubmitOrder));
DuringAny(
When(OrderCompleted)
.TransitionTo(Completed));
SetCompleted(async instance =>
{
var currentState = await this.GetState(instance);
return Completed.Equals(currentState);
});
}
}
Contracts:
public record SubmitOrder(Guid OrderId, DateTime? OrderDate);
public record OrderAccepted(Guid OrderId);
public record OrderCompleted(Guid OrderId);
Consumers:
public class SubmitOrderConsumer: IConsumer<SubmitOrder>
{
public async Task Consume(ConsumeContext<SubmitOrder> context)
{
await Task.Delay(2000);
}
}
public class SubmitOrderConsumerDefinition : ConsumerDefinition<SubmitOrderConsumer>
{
public SubmitOrderConsumerDefinition()
{
EndpointName = "submit-order";
}
protected override void ConfigureConsumer(IReceiveEndpointConfigurator endpointConfigurator, IConsumerConfigurator<SubmitOrderConsumer> consumerConfigurator)
{
endpointConfigurator.ConfigureConsumeTopology = false;
}
}
Web API
Program.cs (snippet)
// Add services to the container.
builder.Services.AddMassTransit(cfg =>
{
cfg.SetKebabCaseEndpointNameFormatter();
cfg.UsingRabbitMq((context, configurator) =>
{
configurator.Host("localhost", "/", hostConfigurator =>
{
hostConfigurator.Username("guest");
hostConfigurator.Password("guest");
});
});
});
builder.Services.AddMassTransitHostedService();
builder.Services.AddControllers();
OrderController
[Route("order")]
public class OrderController : ControllerBase
{
private readonly ISendEndpointProvider _sendEndpointProvider;
public OrderController(ISendEndpointProvider sendEndpointProvider)
{
_sendEndpointProvider = sendEndpointProvider;
}
[HttpPost]
public async Task<IActionResult> SendOrder()
{
var endpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri("exchange:submit-order"));
await endpoint.Send(new SubmitOrder(Guid.NewGuid(), DateTime.Now));
return Ok();
}
}
Worker Service
Program.cs
using IHost = Microsoft.Extensions.Hosting.IHost;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddMassTransit(cfg =>
{
cfg.AddConsumer<SubmitOrderConsumer>(typeof(SubmitOrderConsumerDefinition));
cfg.AddSagaStateMachine<OrderStateMachine, OrderState>().InMemoryRepository();
cfg.UsingRabbitMq((context, rabbitMqConfigurator) =>
{
rabbitMqConfigurator.Host("localhost", "/", hostConfigurator =>
{
hostConfigurator.Username("guest");
hostConfigurator.Password("guest");
});
rabbitMqConfigurator.ReceiveEndpoint("saga-order", endpointConfigurator =>
{
endpointConfigurator.ConfigureSaga<OrderState>(context);
});
rabbitMqConfigurator.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService();
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
Then I do a POST via Postman to: http://localhost:5000/order
It does call the SubmitOrderConsumer, but for some reason, the State machine does not get invoked (it won't hit breakpoint inside the Then handler that sets the Order Date inside Initially state.). I think I am missing something that connects the two together.
Any feedback is greatly appreciated. Thank you.
In your example, you'd want to use Publish, especially in this scenario where you have two consumers (the consumer, and the state machine) on separate endpoints (queue) that would be consuming the message. Sending directly to the exchange would only get the message to one of the endpoints.
I want to implement state management into an application Blazor assembly, so I added Fluxor as library.
I've created an abstract root state which contain the shared propreties:
public abstract class RootState
{
public RootState(bool isLoading, string currentErrorMessage)
=> (IsLoading, CurrentErrorMessage) = (isLoading, currentErrorMessage);
public bool IsLoading { get; }
public string CurrentErrorMessage { get; }
public bool HasCurrentErrors => !string.IsNullOrWhiteSpace(CurrentErrorMessage);
}
Then, I created the Login State which inherits from Rootstate:
public class LoginState : RootState
{
public bool IsAuthenticated { get; set; }
public LoginResponseDto UserData { get; set; }
public LoginState(bool isLoading, string currentErrorMessage, bool isAuthenticated, LoginResponseDto userData)
:base(isLoading, currentErrorMessage)
{
IsAuthenticated = isAuthenticated;
UserData = userData;
}
}
After that, I created the feature class :
public class LoginFeature : Feature<LoginState>
{
public override string GetName() => nameof(LoginState);
protected override LoginState GetInitialState()
=> new LoginState(false, null, false, null);
}
Then the actions:
public class LoginAction
{
public LoginDto LoginDto { get; }
public LoginAction(LoginDto LoginDto)
{
this.LoginDto = LoginDto;
}
}
public class LoginAccessFailure : FailureAction
{
public LoginAccessFailure(string errorMessage)
:base(errorMessage)
{
}
}
public abstract class FailureAction
{
public string ErrorMessage { get; }
protected FailureAction(string errorMessage) => ErrorMessage = errorMessage;
}
After that I created the Reducer and Effect:
public class LoginReducer
{
[ReducerMethod]
public static LoginState ReduceLoginAction(LoginState state, LoginAction action)
=> new LoginState(true, null, false, state.UserData);
[ReducerMethod]
public static LoginState ReduceLoginSuccessAction(LoginState state, LoginActionSuccess action)
{
Console.WriteLine("State from Reducer", action);
var result = new LoginState(false, null, action.IsAuthenticated, action.UserData);
Console.WriteLine("Result from Reducer", result);
return result;
}
[ReducerMethod]
public static LoginState ReduceLoginFailureAction(LoginState state, LoginAccessFailure action)
=> new LoginState(false, action.ErrorMessage, false, null);
}
Effect:
public class LoginEffect : Effect<LoginAction>
{
private readonly IAccountService _accountService;
public LoginEffect(IAccountService accountService)
{
_accountService = accountService;
}
public override async Task HandleAsync(LoginAction action, IDispatcher dispatcher)
{
try
{
var loginResponse = await _accountService.Login(action.LoginDto);
await Task.Delay(TimeSpan.FromMilliseconds(1000));
dispatcher.Dispatch(new LoginActionSuccess(loginResponse, true));
}
catch (Exception e)
{
dispatcher.Dispatch(new LoginAccessFailure(e.Message));
}
}
}
When I want to call the dispatcher with this instruction dispatcher.Dispatch(new LoginAction(new LoginDto { Email = "test#email.com", Password = "test" })); I've got the result in the DevTools but there is not data in IState<LoginState> so I can't access to the state.
There is any error on my code please ?
Things to check
You have [Inject] before your private IMyState<T> MyState { get; set; }
You have a <StoreInitializer/> component in your main app component.
Your component descends from FluxorComponent or you manually subscribe to MyState.StateChanged and call InvokeAsync(StateHasChanged)
I have implement MediatR in .net 5 application and want to resolve require dependencies using Handler interfaces. Currently I using class name to resolve as following
_mediator.Send(new GetDeviceByIMEI(imei)); // want to use interface ??
//var result = await _mediator.Send(IGetHandHeldByIMEI????);
full code reference as following;
Handler Interface
public interface IGetDeviceByIMEIEventHandler
{
Task<DeviceWrapperDataView> Handle(GetDeviceByIMEI request, CancellationToken cancellationToken);
}
Query Interface
public interface IGetDeviceByIMEI
{
string IMEI { get; set; }
}
Query
public class GetDeviceByIMEI: IRequest<DeviceWrapperDataView>
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
Handler
public class GetDeviceByIMEIEventHandler : IRequestHandler<GetDeviceByIMEI, DeviceWrapperDataView>, IGetDeviceByIMEIEventHandler
{
private readonly IDeviceEntity _DeviceEntity;
public GetDeviceByIMEIEventHandler(IDeviceEntity DeviceEntity)
{
_DeviceEntity = DeviceEntity;
}
public async Task<DeviceWrapperDataView> Handle(GetDeviceByIMEI request, CancellationToken cancellationToken)
{
// code to get data
return DeviceOutput;
}
}
API controller
private readonly IMediator _mediator;
public DeviceController(
IMediator mediator)
{
_mediator = mediator;
}
[HttpGet()]
public async Task<IActionResult> GetDeviceByIMEI(string imei)
{
Var result = await _mediator.Send(new GetDeviceByIMEI(imei));
// want to use
}
To do that you have to register your handler in the container with each of the class that inherit your query interface.
For instance with the code you provided.
public interface IGetDeviceByIMEI : IRequest<DeviceWrapperDataView>
{
string IMEI { get; set; }
}
public class GetDeviceByIMEI: IGetDeviceByIMEI
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
public class AnotherGetDeviceByIMEI: IGetDeviceByIMEI
{
public string IMEI { get; set; }
public GetDeviceByIMEI(string imei)
{
this.IMEI = imei;
}
}
public class GetDeviceByIMEIEventHandler : IRequestHandler<IGetDeviceByIMEI, DeviceWrapperDataView>, IGetDeviceByIMEIEventHandler
{
private readonly IDeviceEntity _DeviceEntity;
public GetDeviceByIMEIEventHandler(IDeviceEntity DeviceEntity)
{
_DeviceEntity = DeviceEntity;
}
public async Task<DeviceWrapperDataView> Handle(IGetDeviceByIMEI request, CancellationToken cancellationToken)
{
// code to get data
return DeviceOutput;
}
}
Once you have done that, you have to register the handler in your container with each use case.
For instance in .Net Core, you can do it with the serviceCollection in the StartUp class.
serviceCollection.AddTransient<IRequestHandler<GetDeviceByIMEI, DeviceWrapperDataView>, GetDeviceByIMEIEventHandler >();
serviceCollection.AddTransient<IRequestHandler<AnotherGetDeviceByIMEI, DeviceWrapperDataView>, GetDeviceByIMEIEventHandler >();
Regards.
I am creating a simple logging system by using Nest and C#. I have a log producer for collecting logs inside of blockingcollection. Also I have a consumer. But I stumpled upon with an issue. How can I use my comsumer in startup or is there any way to create background servise which was listening queue of blockingcollection? What is best practice of it? I am confusing how to call AsyncConsumer or consumer when application startup.
public class SimpleLog
{
public string Header { get; set; }
public string LogDate { get; set; }
public string Sessionid { get; set; }
public string Userid { get; set; }
public string Correlationid { get; set; }
public int Status { get; set; }
public string UrlQueryString { get; set; }
public string UrlPath { get; set; }
public string UrlMethod { get; set; }
public string Environment { get; set; }
public string IndexName { get; set; }
public string IndexType { get; set; }
}
public class QuickLog
{
private static BlockingCollection<SimpleLog> data = new BlockingCollection<SimpleLog>();
public static void Producer(SimpleLog pageviewLog)
{
data.TryAdd(pageviewLog, TimeSpan.FromSeconds(10));
}
public static void Consumer()
{
var _client = ElasticConfig.GetClient();
var logs = new List<SimpleLog>();
foreach (var item in data.GetConsumingEnumerable())
{
logs.Add(item);
}
if (logs == null && logs.Count <= 0)
return;
var log = logs.FirstOrDefault();
var response = _client.IndexMany(logs, log.IndexName, log.IndexType);
if (!response.IsValid)
throw response.OriginalException;
}
public async Task AsyncConsumer()
{
var _client = ElasticConfig.GetClient();
var logs = new List<SimpleLog>();
foreach (var item in data.GetConsumingEnumerable())
{
logs.Add(item);
}
if (logs == null && logs.Count <= 0)
return;
var log = logs.FirstOrDefault();
var response = await _client.IndexManyAsync(logs, log.IndexName, log.IndexType).ConfigureAwait(false);
if (!response.IsValid)
throw response.OriginalException;
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
}
}
public static class ElasticConfig
{
private static IElasticClient _client;
static ElasticConfig()
{
var esurl = LogSettings.Url;
string[] urls = esurl.Split(',');
var nodes = new Uri[2];
for (int i = 0; i < urls.Length; i++)
{
nodes.SetValue(new Uri(urls[i]), i);
}
var connectionPool = new SniffingConnectionPool(nodes);
var connectionSettings = new ConnectionSettings(connectionPool).RequestTimeout(
TimeSpan.FromSeconds(60))
.PingTimeout(TimeSpan.FromSeconds(60))
.MaxRetryTimeout(TimeSpan.FromSeconds(60))
.MaxDeadTimeout(TimeSpan.FromSeconds(60))
.DeadTimeout(TimeSpan.FromSeconds(60)).DisablePing()
.SniffOnConnectionFault(false)
.SniffOnStartup(false)
.SniffLifeSpan(TimeSpan.FromMinutes(1));
_client = new ElasticClient(connectionSettings);
}
public static IElasticClient GetClient()
{
return _client;
}
}
Not sure how many times and which method exactly you want to call. If you want to run some asynchronous background jobs you can use IHostedService. You will need to install Microsoft.Extensions.Hosting NuGet package or Microsoft.AspNetCore.App metapackage.
Usage:
Add this line to your Startup.cs
services.AddHostedService<LogBackgroundService>(); //service is instance of IServiceCollection
And this is the implementation of your background service:
public class LogBackgroundService : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
await QuickLog.AsyncConsumer(); // or whatever you want to call
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Behavior:
It will run once(but IHostedService still will be running. If you want to reduce resource consumption just call StopAsync() when it's done). If you want to run something in a loop, you can implement this:
while (!cancellationToken.IsCancellationRequested)
{
await QuickLog.AsyncConsumer();
await Task.Delay(250, cancellationToken); // you can add this if you want to throttle
}
PS. If you need to run multiple IHostedServices in your application without blocking each other you will need to wrap your methods into Tasks:
public Task StartAsync(CancellationToken cancellationToken)
{
Task.Run(() => QuickLog.AsyncConsumer(), cancellationToken);
}
IHostedService is solution for you.
You can crate new class and inherit from this class https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3
Then your new class will be something like this
public class LogService : HostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public LogBackgroundService (IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await new QuickLog().AsyncConsumer(cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
}
}
}
Finally update your Startup.cs:
services.AddSingleton<IHostedService, LogService>();
I am working with Sagas in ReBus and from my experience with NServiceBus you could reply to the original creator of the Saga to give updates, something like this:
Saga<>.ReplyToOriginator
I do not see an equivalent way of doing this with ReBus. Is there a way to do this, and if not what is a good pattern (other than the originator polling) I can use that will achieve the same thing? An example is trying to create a customer and the client wanting to know when it is created before trying to change it's address.
Here is a trivial example of the Customer scenario I quickly put together:
public class CreateCustomerSaga : Saga<CreateCustomerData>,
IAmInitiatedBy<CreateCustomerCommand>,
IHandleMessages<CustomerUniqunessCheckResult>
{
private readonly IBus _bus;
private readonly ICustomerResourceAccess _customerResourceAccess;
public CreateCustomerSaga(IBus bus, ICustomerResourceAccess customerResourceAccess)
{
_bus = bus;
_customerResourceAccess = customerResourceAccess;
}
public override void ConfigureHowToFindSaga()
{
Incoming<CustomerUniqunessCheckResult>(x => x.IsCustomerUnique).CorrelatesWith(y => y.CustomerId);
}
public void Handle(CreateCustomerCommand message)
{
Data.CustomerId = message.CustomerId;
Data.CustomerName = message.CustomerName;
_bus.Send(new CheckCustomerUniquenessCommand(message.CustomerId));
}
public void Handle(CustomerUniqunessCheckResult message)
{
if (message.IsCustomerUnique)
{
_customerResourceAccess.CreateCustomer(Data.CustomerId, Data.CustomerName);
// This is what seems to be missing from ReBus to reply to the original sender
_bus.?(new CustomerCreatedEvent(Data.CustomerId));
}
else
{
// This is what seems to be missing from ReBus to reply to the original sender
_bus.?(new CustomerAlreadExistsEvent(Data.CustomerId));
}
}
}
public class CustomerCreatedEvent
{
public Guid CustomerId { get; set; }
public CustomerCreatedEvent(Guid customerId)
{
CustomerId = customerId;
}
}
public class CustomerAlreadExistsEvent
{
public Guid CustomerId { get; set; }
public CustomerAlreadExistsEvent(Guid customerId)
{
CustomerId = customerId;
}
}
public class CustomerUniqunessCheckResult
{
public bool IsCustomerUnique { get; set; }
}
public class CheckCustomerUniquenessCommand
{
public CheckCustomerUniquenessCommand(Guid customerId)
{ }
}
public interface ICustomerResourceAccess
{
void CreateCustomer(Guid customerId, string customerName);
}
public class CreateCustomerCommand
{
public Guid CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class CreateCustomerData : ISagaData
{
public CreateCustomerData()
{
Id = Guid.NewGuid();
}
public Guid CustomerId { get; set; }
public string CustomerName { get; set; }
public Guid Id { get; set; }
public int Revision { get; set; }
}
No, unfortunately there's no reply to originator function in Rebus' sagas at the moment. You can easily do it though, by storing the originator's endpoint when your saga is created like this (in all Handle methods of messages that can initiate the saga):
if (IsNew) {
Data.Originator = MessageContext.GetCurrent().ReturnAddress;
}
and then when you want to reply back to the originator:
bus.Advanced.Routing.Send(Data.Originator, new HelloThereMyFriend());
I've often thought about adding this to Rebus though, either as an extra field on ISagaData, or as an extra interface ISagaDataWithOriginator that you could optionally apply to your saga data, but I've never had the need (enough) myself.
If each sender is a separate (virtual) machine, like in my implementation, you could get a guaranteed reply to originator by using their machine unique ID or MAC address in the reply queue name, like this:
Bus = Configure.With(adapter)
.Transport(t => t.UseSqlServer(DbConfiguration.DatabaseConnectionString,
sInstanceId, sInstanceId + ".Error")
.EnsureTableIsCreated())
...
If each originator has a unique queue ID, the consumer can reply to originator simply by using Bus.Reply.
The unique machine id can be determined using System.Management:
string uuid = string.Empty;
ManagementClass mc = new System.Management.ManagementClass("Win32_ComputerSystemProduct");
if (mc != null)
{
ManagementObjectCollection moc = mc.GetInstances();
if (moc != null)
{
foreach (ManagementObject mo in moc)
{
PropertyData pd = mo.Properties["UUID"];
if (pd != null)
{
uuid = (string)pd.Value;
break;
}
}
}
}
Or by using the MAC address of the machine (our fallback code):
if (string.IsNullOrEmpty(uuid))
{
uuid = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
.FirstOrDefault()
.GetPhysicalAddress().ToString();
}