NServiceBus.Persistence.Sql
NServiceBus.Persistence.Sql copied to clipboard
Saga script generation fails if message mapping maps to a field
Saga script generation will fail on this saga:
public class MySaga : Saga<MyData>,
IAmStartedByMessages<Msg>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<MyData> mapper)
{
mapper.ConfigureMapping<Msg>(msg => msg.CorrId).ToSaga(saga => saga.ScriptRunId);
}
public Task Handle(Msg message, IMessageHandlerContext context) => throw new NotImplementedException();
}
public class MyData : ContainSagaData
{
public Guid ScriptRunId { get; set; }
}
public class Msg : ICommand
{
public Guid CorrId;
}
The cause is that Msg.CorrId is a field, and not a property.
A property would generate the following IL:
IL_000C: ldstr "msg"
IL_0011: call System.Linq.Expressions.Expression.Parameter
IL_0016: stloc.0
IL_0017: ldloc.0
IL_0018: ldtoken UserQuery+Msg.get_CorrId
IL_001D: call System.Reflection.MethodBase.GetMethodFromHandle
IL_0022: castclass System.Reflection.MethodInfo
IL_0027: call System.Linq.Expressions.Expression.Property
While with a field, it generates:
IL_000C: ldstr "msg"
IL_0011: call System.Linq.Expressions.Expression.Parameter
IL_0016: stloc.0
IL_0017: ldloc.0
IL_0018: ldtoken UserQuery+Msg.CorrId
IL_001D: call System.Reflection.FieldInfo.GetFieldFromHandle
IL_0022: call System.Linq.Expressions.Expression.Field
The last 2 call instances for the field are not included on the list of allowed calls so the instruction analyzer assumes that the user is calling out to a separate method, and if the mapper were passed to that method, all sorts of extra mapping could be going on behind the back of the instruction analyzer which can only look at the IL for the one method.
Whether or not fields are appropriate for messages matters more on the choice of serializer than anything else. The saga script generator shouldn't fail because of it.
Workaround
Don't use fields in messages.