使用NServiceBus(v6),有没有办法确保在触发消息的Saga Handler之前在SagaData对象中设置属性?
我们的环境是多租户的,所以我想确保将正确的CustomerId用于数据库访问等。并且开发人员不要忘记从传入的消息/消息头中提取此值。
例如,考虑到这个传奇数据...
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
}
......以及以下的传奇...
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 ...
}
我最初尝试在管道中插入一个行为,例如
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;
}
}
}
}
}
...但这只允许在处理程序被触发后访问SagaData实例。
答案 0 :(得分:1)
迟到的答案,但你需要确保你的行为在SagaPersistenceBehavior之后执行。
在您的IConfigureThisEndpoint实现中:
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");
}
}
答案 1 :(得分:0)
因此,在处理StartProcess消息时,不会直接回答您的问题Data.CustomerId。您需要使用消息中的ID设置它。
public async Task Handle(StartProcess message, IMessageHandlerContext context)
{
Data.CustomerId = message.CustomerId;
}
据说上面的示例缺少一个关键部分,这是确定如何查找传奇以继续处理的代码:
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SomeProcessSagaData> mapper)
{
mapper.ConfigureMapping<StartProcess>(message => message.CustomerId)
.ToSaga(sagaData => sagaData.CustomerId);
}
每次发送由saga处理的消息类型时,您需要配置ConfigureHowToFindSaga()
方法,以便它可以查找以前启动的saga以继续处理。因此,从本质上讲,您将使用StartProcess
消息为您发送的每个客户ID 开始一个新的传奇。您可以在此处详细了解:https://docs.particular.net/nservicebus/sagas/
所以现在真正的问题是你真的需要在这一点上使用传奇吗?该示例似乎只处理一种类型的消息,所以您真的需要保存CustomerId的状态吗?你的样本中没有必要使用saga的开销,我相信基于上面的例子,常规处理程序就可以了。