SqlSaga
is an alternate saga base class for use with SQL persistence that offers a less verbose mapping API than NServiceBus.
. It's generally advisable to use NServiceBus.
by default for most new projects, and to switch to SqlSaga
when advantageous to cut down on the need for repetitive .
expressions in sagas that handle several message types.
SqlSaga definition
A saga can be implemented using the SqlSaga
base class as follows:
public class OrderSaga :
SqlSaga<OrderSaga.SagaData>,
IAmStartedByMessages<StartOrder>
{
static ILog log = LogManager.GetLogger<OrderSaga>();
protected override void ConfigureMapping(IMessagePropertyMapper mapper)
{
mapper.ConfigureMapping<StartOrder>(message => message.OrderId);
}
protected override string CorrelationPropertyName => nameof(SagaData.OrderId);
public Task Handle(StartOrder message, IMessageHandlerContext context)
{
log.Info($"Received StartOrder message {Data.OrderId}.");
MarkAsComplete();
return Task.CompletedTask;
}
public class SagaData :
ContainSagaData
{
public Guid OrderId { get; set; }
}
}
Note that there are differences to how a standard NServiceBus saga is implemented.
Correlation IDs
Instead of inferring the correlation property from multiple calls to .
, the SqlSaga
base class identifies the correlation property once as a class-level property.
Single correlation ID
In most cases there will be a single correlation ID per Saga Type.
public class SagaWithCorrelation :
SqlSaga<SagaWithCorrelation.SagaData>,
IAmStartedByMessages<StartSagaMessage>
{
protected override void ConfigureMapping(IMessagePropertyMapper mapper)
{
mapper.ConfigureMapping<StartSagaMessage>(message => message.CorrelationProperty);
}
protected override string CorrelationPropertyName => nameof(SagaData.CorrelationProperty);
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
{
return Task.CompletedTask;
}
public class SagaData :
ContainSagaData
{
public Guid CorrelationProperty { get; set; }
}
}
Correlation and transitional IDs
During the migration from one correlation ID to another correlation ID, there may be two correlation IDs that co-exist. See also the Transitioning Correlation IDs sample.
public class SagaWithCorrelationAndTransitional :
SqlSaga<SagaWithCorrelationAndTransitional.SagaData>,
IAmStartedByMessages<StartSagaMessage>
{
protected override void ConfigureMapping(IMessagePropertyMapper mapper)
{
mapper.ConfigureMapping<StartSagaMessage>(message => message.CorrelationProperty);
}
protected override string CorrelationPropertyName => nameof(SagaData.CorrelationProperty);
protected override string TransitionalCorrelationPropertyName => nameof(SagaData.TransitionalCorrelationProperty);
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
{
return Task.CompletedTask;
}
public class SagaData :
ContainSagaData
{
public Guid CorrelationProperty { get; set; }
public Guid TransitionalCorrelationProperty { get; set; }
}
}
No correlation ID
When implementing a custom saga finder it is possible to have a message that does not map to a correlation ID and instead interrogates the JSON-serialized data stored in the database.
public class SagaWithNoMessageMapping :
SqlSaga<SagaWithNoMessageMapping.SagaData>
{
protected override void ConfigureMapping(IMessagePropertyMapper mapper)
{
}
protected override string CorrelationPropertyName => null;
public class SagaData :
ContainSagaData
{
}
}
Table suffix
A saga's table suffix, which forms part of the table name in the database, can also be defined more easily using a property override when using the SqlSaga
base class:
class MySaga:SqlSaga<MySaga.SagaData>
{
protected override string TableSuffix => "TheCustomTableName";