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";