Same Rebus handler instance for multiple messages within a Unit of Work

StackOverflow https://stackoverflow.com/questions/15615961

  •  29-03-2022
  •  | 
  •  

Pergunta

I wish to process related messages in a batch e.g. processing the events CustomerCreated and PreferredCustomer with the same handler (same instance) within the same scope/transaction using the Rebus service bus.

The same handler is handling both messages/events:

class CustomerHandler : IHandleMessages<CustomerCreated>, IHandleMessages<PreferredCustomer>
{
    Customer Customer { get; set; }

    public CustomerHandler() {
        Customer = new Customer();
    }

    public void Handle(CustomerCreated message) {
        Customer.Name = message.Name;
        Console.WriteLine(Customer);
    }

    public void Handle(PreferredCustomer message) {
        Customer.Rebate = message.Rebate;
        Console.WriteLine(Customer);
    }
}

When sending the messages I use the batch operation (transport messages in NServiceBus)

bus.Advanced.Batch.Publish(
  new CustomerCreated() { Name = "Anders" }, 
  new PreferredCustomer() { Rebate = 10 });

To control the lifetime of the handler, I use Windsor Castle’s Scoped lifestyle

_container.Register(
  Component.For<IHandleMessages<CustomerCreated>, IHandleMessages<PreferredCustomer>>)
    .ImplementedBy<CustomerHandler>().LifestyleScoped());

And a custom UnitOfWorkManager that instanciates the ScopedUnitOfWork

class CustomUnitOfWorkManager : IUnitOfWorkManager
{
    private readonly IWindsorContainer _container;

    public CustomUnitOfWorkManager(IWindsorContainer container) {
        _container = container;
    }

    public IUnitOfWork Create() {
        return new ScopedUnitOfWork(_container);
    }
}

class ScopedUnitOfWork : IUnitOfWork
{
    private readonly IDisposable _scope;

    public ScopedUnitOfWork(IWindsorContainer container) {
        // Begin transaction
        _scope = container.BeginScope();

    }

    public void Dispose() {
        _scope.Dispose();
    }

    public void Commit() {
        // Commit transaction
        Console.WriteLine("Commiting");
    }

    public void Abort() {
        // Rollback transaction            
        Console.WriteLine("Aborting!!!");
    }
}

Finally configured Rebus to use the CustomUnitOfWorkManager

var bus = Configure.With(new WindsorContainerAdapter(_container))
  .Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())
  .MessageOwnership(d => d.FromRebusConfigurationSection())
  .Events(x => x.AddUnitOfWorkManager(new CustomUnitOfWorkManager(_container)))
  .CreateBus()
  .Start();

Is this the correct approach?

My limited testing shows that this should work. I am even able to expand this to include transaction management against the data store within the ScopedUnitOfWork too.

Foi útil?

Solução

Sounds like you have it nailed :)

If you get the correct commit/rollback behavior, then I'd say it's fine and dandy.

If you're interested in an alternative, you might want to take a look at the PerTransportMessage Castle Windsor scope accessor - it can be used like this:

container.Register(
    Component
        .For<ISomething>()
        .ImplementedBy<Whatever>()
        .LifestyleScoped<PerTransportMessage>()
);

which should be able to achieve the exact same behavior.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top