NserviceBus not loading existing saga data - nservicebus

I have implemented a my connector using nservice bus saga. Below is the code
public class ClientSaga : Saga<ClientSagaState>,
IAmStartedByMessages<ClientChangeMessage>,
IAmStartedByMessages<ClientContactChangeMessage>,
IAmStartedByMessages<ClientPictureChangeMessage>,
IHandleTimeout<ClientSagaState>
{
[SetterProperty]
public IClientContactChangeDb ClientContactChangeDb{get;set;}
[SetterProperty]
public IBusRefTranslator BusRefTranslator{get;set;}
public void Handle(ClientContactChangeMessage message)
{
var state=this.Data;
//Some handling logic
//Check if client is not in database then store the state
state.ClientContactChange=message;
state.ClientRef =message.ClientRef;
//if client is in the data base then
MarkAsComplete();
}
public void Handle(ClientChangeMessage message)
{
var state=this.data;
//Update or create the client depending on the situation
//check for dependencies
if(state.ClientContactChange !=null)
{
//Handle the contact change
}
else
{
state.ClientChangeMessage=message;
state.ClientRef=message.ClientRef;
}
}
public void Handle(ClientPictureChangeMessage message)
{
var state=this.Data;
//If the client is there then update the picture else store in saga
state.ClientPictureChangeMessage =message;
state.ClientRef=message.ClientRef;
}
}
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<ClientContactChangeMessage>(s => s.ClientRef, m => m.ClientRef);
ConfigureMapping<ClientPictureChangeMessage>(s => s.ClientRef, m => m.ClientRef);
ConfigureMapping<ClientChangeMessage>(s => s.ClienttnRef, m => m.Id);
}
}
public class ClientSagaState: IContainSagaData
{
//i dont need these three fields
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
// the fields which i needed
public Guid ClientRef {gee; set;}
public ClientChangeMessage ClientChange {get;set;}
public ClientContactChange ClientContactChange {get;set;}
public ClientPictureChangeMessage ClientPictureChangeMessage {get;set;}
}
Now in my connector a client cannot be created w/o client contact change message being present.
Case when saga fails:
When i send the the client picture message first it creates a new
saga and stores it.
Then i send a client change message it creates another saga and
stores it i.e does not find the saga created by the client picture
message
Then i send the client contact change message it somehow finds the
saga created by client picture change but now cannot find the staff.
I can't make out why this is happening.
Case when saga succeeds:
When i send the client change message first it creates the saga.
Then i send the client contact change message it finds the saga
and executes fine.
Can anyone please explain why this behaviour is happening.
Please let me know if more information is needed.
Thanks
UPDATE
On checking my code again, i found the cause of this . My ClientChangeMessage was also inheriting from IContainSaga data(something which i was trying out but had forgotten to remove). After removing the inheritance link everything was working fine. (Head hanging in shame)

In all your handlers, you need to set the ClientRef on the Saga Data.
So, you would have:
public void Handle(ClientContactChangeMessage message)
{
Data.ClientRef = message.ClientRef
...
}
As any of these messages can start the saga, you'll need to set this value in your saga state. When other messages come in, then it will be co-rrelated by this id as there is already an instance of the saga with this Id.
To refer to your saga state variables, use Data. intead of this.

Related

Can SagaData be manipulated before the saga handler fires using NServiceBus

Using NServiceBus (v6), is there a way to ensure that a property is set in the SagaData object before the Saga Handler for a message is fired?
Our environment is multi-tenant so I want to ensure that the correct CustomerId is used for db access etc.. and that developers don't forget to pull this value from the incoming message/message header.
For example given this saga data ...
public interface ICustomerSagaData : IContainSagaData
{
Guid CustomerId { get; set; }
}
public class SomeProcessSagaData : ICustomerSagaData
{
// IContainSagaData and other properties removed for brevity ...
#region ICustomerSagaData properties
public virtual Guid CustomerId { get; set; }
#endregion
}
... and the following Saga ...
public class SomeProcessSagaSaga :
Saga<SomeProcessSagaData>,
IAmStartedByMessages<StartProcess>
{
public async Task Handle(StartProcess message, IMessageHandlerContext context)
{
// How do I ensure that Data.CustomerId is already set at this point?
}
// ConfigureHowToFindSaga etc ...
}
I initially tried inserting a behaviour into the pipeline e.g.
public class MyInvokeHandlerBehavior : Behavior<IInvokeHandlerContext>
{
public override async Task Invoke(IInvokeHandlerContext context, Func<Task> next)
{
// Ideally I'd like to set the CustomerId here before the
// Saga Handler is invoked but calls to ...
// context.Extensions.TryGet(out activeSagaInstance);
// return a null activeSagaInstance
await next().ConfigureAwait(false);
// This is the only point I can get the saga data object but
// as mentioned above the hander has already been invoked
ActiveSagaInstance activeSagaInstance;
if (context.Extensions.TryGet(out activeSagaInstance))
{
var instance = activeSagaInstance.Instance.Entity as ICustomerSagaData;
if (instance != null)
{
Guid customerId;
if (Guid.TryParse(context.Headers["CustomerId"), out customerId))
{
instance.CustomerId = customerId;
}
}
}
}
}
... but this only allows access to the SagaData instance after the handler has been fired.
Late answer, but you need to make sure your behaviour executes after the SagaPersistenceBehavior.
In your IConfigureThisEndpoint implementation:
public virtual void Customize(EndpointConfiguration configuration)
{
configuration.Pipeline.Register<Registration>();
}
public class Registration : RegisterStep
{
public Registration()
: base(
stepId: "AuditMutator",
behavior: typeof(AuditMutator),
description: "Sets up for auditing")
{
this.InsertAfterIfExists("InvokeSaga");
}
}
So to answer your question directly Data.CustomerId is not going to be set when you handle StartProcess messages. You will need to set that with the id coming off of the message.
public async Task Handle(StartProcess message, IMessageHandlerContext context)
{
Data.CustomerId = message.CustomerId;
}
That being said your sample above is missing a crucial piece which is the code for determining how a saga can be looked up for continuation of processing:
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SomeProcessSagaData> mapper)
{
mapper.ConfigureMapping<StartProcess>(message => message.CustomerId)
.ToSaga(sagaData => sagaData.CustomerId);
}
Each time you send a message type that is handled by a saga you need to have the ConfigureHowToFindSaga() method configured so it can look up the previously started saga to continue processing with. So in essence you are going to start a new saga for every customerid you send with a StartProcess message. You can read more about it here: https://docs.particular.net/nservicebus/sagas/
So the real question now is do you really need to be using a saga at this point? The sample only seems to be handling one type of message so do you really need to be saving the state of CustomerId? The overhead of the saga isn't necessary in your sample and I believe a regular handler would be just fine based on the example above.

REBUS Send message in Queue ,and receive in another application

I want to implement following scenario using rebus. I am creating on sender application and one receiving application. There will be a class suppose
public class GetPersonRequest
{
public int Id { get; set; }
public string Name { get; set; }
}
public class GetPersonResponse
{
public int Id { get; set; }
public string Name { get; set; }
}
I will send this class object in queue with values. And want to display those value in receiver. How to achieve this?
SENDER code like this:
static void Main(string[] args)
{
GetPersonRequest objGetPersonRequest = new GetPersonRequest();
objGetPersonRequest.Id = 12;
objGetPersonRequest.Name = "Kumar";
using (var activator = new BuiltinHandlerActivator())
{
activator.Register(() => new PrintName());
var bus = Configure.With(activator)
.Logging(l => l.None())
.Transport(t => t.UseMsmq("rebus-application.input"))
.Routing(r => r.TypeBased().Map<GetPersonRequest>("rebus.application.output"))
.Start();
bus.Send(objGetPersonRequest);
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
RECEIVER Code like this in another console application:
static void Main(string[] args)
{
using (var activator = new BuiltinHandlerActivator())
{
activator.Register(() => new PrintName());
var bus = Configure.With(activator)
.Logging(l => l.None())
.Transport(t => t.UseMsmq("rebus-application.output"))
.Routing(r => r.TypeBased().Map<GetPersonResponse>("rebus-application.input"))
.Start();
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
class PrintName : IHandleMessages<GetPersonResponse>
{
public async Task Handle(GetPersonResponse objGetPersonResponse)
{
Console.WriteLine("RebusDetails Name is {0}", objGetPersonResponse.Name);
}
}
How to achieve this?
I suggest you take a look at the request/reply sample from the RebusSamples repository - it shows the configuration needed in order to do proper request/reply.
From quickly glancing over your code, I can see the following issues/misunderstandings:
Rebus methods are asynchronous, hence bus.Send(objGetPersonRequest) will execute on another thread and you will not know if it failed - always either await bus.Send(...) or bus.Send(...).Wait()
In many cases, only "clients" (*) should have endpoint mappings - in your case, you should map GetPersonRequest (or possibly the entire assembly containing it?) to rebus.application.output, and then do an await bus.Reply(new GetPersonResponse(...)) in the handler - this way, the "server"(*) will not have any dependencies
Moreover - this might be a detail, but I think it leads to a better understanding and easier communication over time:
There's no such thing as an "output queue" - all queues are the input queue of the endpoint that has it as its input queue - therefore, I would argue that the name rebus-application.output is misleading
I suggest you change your queue names to something that identifies each endpoint better, e.g. since your server seems to be capable of returning a person's details, you could call it masterdata, crm, etc., possibly suffixing .input if you e.g. want to have an error queue for each endpoint (e.g. masterdata.input and masterdata.error).
I hope that makes sense :)
(*) In my experience, it's beneficial to have a pretty clear distinction between client and server roles for your endpoints, where clients are endpoints with no (or very few) afferent couplings, which allows for them to easily be added/removed/changed, and servers are endpoints with more afferent couplings.
When you await bus.Reply(...) from a server, it allows for the sender to remain a client and not have its endpoint address configured anywhere but in its own configuration.

How to get the total number of subscribers in NServiceBus?

I'm using NServiceBus and I need to know how many clients are subscribed to a specific message type (even better the names of the subscribers). I'm talking in a pub\sub scenario.
Is it possible to get this information in NServiceBus?
Thx
You can pull this right out of your subscription storage. Either a query to the database or a .GetAllMessages() on the queue will get you a count and the subscribers address. If you are looking to do this in code, you could write a handler for the subscription message and count them up that way.
I have used ISubscriptionStorage with success.
public class SubscribersForMessageHandler :
IHandleMessages<SubscribersForMessageRequest>
{
public ISubscriptionStorage Storage { get; set; }
public IBus Bus { get; set; }
public void Handle(SubscribersForMessageRequest message)
{
Bus.Reply<SubscribersForMessageResponse>(m=>
{
m.SagaId = message.SagaId;
m.MessageType = message.MessageType;
m.SubscriberEndpoints = GetSubscribersForMessage(message.MessageType);
});
}
private List<string> GetSubscribersForMessage(string type)
{
return Storage.GetSubscribersForMessage(
new List<string> { type }).ToList();
}
}

Impossible to have two sagas that handle the same message type

I have 2 different sagas (I mean saga types) that handle the same message.
public class AttachMessageToBugSaga : TpSaga<AttachMessageToBugSagaData>, IAmStartedByMessages<MessageIsNotAttached>, IHandleMessages<MessageAttachedToGeneralMessage>
{
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<MessageAttachedToGeneralMessage>(
saga => saga.Id,
message => message.SagaId
);
}
public void Handle(MessageIsNotAttachedToBug message)
{
Send(new AttachMessageToGeneralCommand { MessageId = 66, GeneralId = 13 });
}
public void Handle(MessageAttachedToGeneralMessage message)
{
//do some stuf fhere
}
}
public class AttachMessageToBugSagaData : IContainSagaData
{
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
}
public class AttachMessageToRequestSaga : TpSaga<AttachMessageToRequestSagaData>, IAmStartedByMessages<MessageIsNotAttachedToRequest>, IHandleMessages<MessageAttachedToGeneralMessage>
{
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<MessageAttachedToGeneralMessage>(
saga => saga.Id,
message => message.SagaId
);
}
public void Handle(MessageIsNotAttachedMessageToRequest message)
{
//do some stuff here
}
public void Handle(MessageAttachedToGeneralMessage message)
{
//do some stuff here
}
}
public class AttachMessageToRequestSagaData : IContainSagaData
{
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
}
When I run the sample I get an exception :
System.InvalidCastException: Unable to cast object of type 'MyCustomPlugin.AttachMessageToGeneralSagaData' to type 'MyCustomPlugin.AttachMessageToRequestSagaData'.
I understand why it happens, but I still need some workaround. I tried to implement my own IFindSagas class :
public class SagaFinder : IFindSagas<AttachMessageToGeneralSagaData>.Using<MessageAttachedToGeneralMessage>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageAttachedToGeneralMessage>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageIsNotAttachedToRequest>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageIsNotAttachedToBug>
{
AttachMessageToGeneralSagaData IFindSagas<AttachMessageToGeneralSagaData>.Using<MessageAttachedToGeneralMessage>.FindBy(MessageAttachedToGeneralMessage message)
{
return ObjectFactory.GetInstance<AttachMessageToGeneralSagaData>();
}
AttachMessageToRequestSagaData IFindSagas<AttachMessageToRequestSagaData>.Using<MessageAttachedToGeneralMessage>.FindBy(MessageAttachedToGeneralMessage message)
{
return ObjectFactory.GetInstance<AttachMessageToRequestSagaData>();
}
public AttachMessageToRequestSagaData FindBy(MessageIsNotAttachedToRequest message)
{
return new AttachMessageToRequestSagaData();
}
public AttachMessageToRequestSagaData FindBy(MessageIsNotAttachedToBug message)
{
return new AttachMessageToRequestSagaData();
}
}
But I do not get into my finders for "MessageAttachedToGeneralMessage".
Please tell me if there is some other workaround, or how to make this example working.
I'm not sure that having more than one Saga within the same process boundary works very well - at least, I've had problems with it too. It's probably better (in general) to have Sagas separated into two different processes anyway, because otherwise it would cause a lot of locking and potentially deadlocks on your saga storage.
Is your message that is handled by 2 Sagas Sent or Published? If it's published (or can be made to), it would be easy to separate the Sagas into two separate assemblies. Just be sure to manually call Bus.Subscribe() for the message type in each Saga, since Sagas don't auto-subscribe to messages listed in the app.config.
If your message is Sent, and there's nothing you can do to change it, then create a central handler for your existing message type that either Publishes a second message type to go to both Sagas, or Sends two separate messages to each saga.
Finally (after digging into the source code) I've found the solution. It seems the only way is to implement my own SagaPersister, where I can do anything I want.
Default implementation in NserviceBus of InMemorySagaPersister has the following code :
T ISagaPersister.Get<T>(Guid sagaId)
{
ISagaEntity result;
data.TryGetValue(sagaId, out result);
return (T)result;
}
And exception occurs while casting .

Are NServiceBus handler's members safe for storing message related (and not related) data?

Are handlers reused to proceed another message?
public abstract class SomeHandler : IHandleMessages<MyEvent>
{
public IBus Bus { get; set; }
public String Message { get; set; }
public void Handle(T message)
{
Message = "Test";
SomeInstanceMethod();
}
public void SomeInstanceMethod()
{
if (Message = ...) // Can i use Message here?
return;
}
}
By default, message handlers are configured as ComponentCallModelEnum.Singlecall, which means that each call on the component will be performed on a new instance.
So, two messages will be processed by different instances of the class and cannot share state.
However, what you have here is setting a class property and then calling another method in the class that retrieves that property. That would work fine. However, in my opinion, that is kind of confusing, and if that is what you're after, you're probably better off passing values to another method as a parameter.