在使用NServiceBus触发saga处理程序之前,是否可以操纵SagaData

在使用NServiceBus触发saga处理程序之前,是否可以操纵SagaData,nservicebus,Nservicebus,使用NServiceBus(v6),是否有办法确保在触发消息的Saga处理程序之前在SagaData对象中设置属性 我们的环境是多租户的,因此我希望确保数据库访问等使用正确的CustomerId。。开发人员不要忘记从传入消息/消息头中提取这个值 例如,根据这段传奇故事的数据 public interface ICustomerSagaData : IContainSagaData { Guid CustomerId { get; set; } } public class SomePr

使用NServiceBus(v6),是否有办法确保在触发消息的Saga处理程序之前在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 ...
}
公共类SomeProcessSagaSaga:
传奇故事
我开始发短信了
{
公共异步任务句柄(StartProcess消息,IMessageHandlerContext)
{
//如何确保此时已设置Data.CustomerId?
}
//配置如何查找Saga等。。。
}
我最初尝试在管道中插入一种行为,例如

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; 
                }
            }
        }
    }
}
公共类MyInvokeHandlerBehavior:Behavior
{
公共重写异步任务调用(IInvokeHandlerContext上下文,Func next)
{
//理想情况下,我希望在
//已调用Saga处理程序,但调用。。。
//context.Extensions.TryGet(out-activeSagaInstance);
//返回空的activeSagaInstance
wait next().configurewait(false);
//这是唯一一点,我可以得到的佐贺数据对象,但
//如上所述,已经调用了hander
主动姿态主动姿态;
if(context.Extensions.TryGet(out-activeSagaInstance))
{
var instance=activeSagaInstance.instance.Entity作为ICCustomerSagaData;
if(实例!=null)
{
Guid客户ID;
if(Guid.TryParse(context.Headers[“CustomerId”),out CustomerId))
{
instance.CustomerId=CustomerId;
}
}
}
}
}

…但这只允许在启动处理程序后访问SagaData实例。因此,要直接回答您的问题,请使用数据。处理StartProcess消息时不会设置CustomerId。您需要使用消息中的id设置该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);
}
protectedoverride void配置如何查找SAGA(SAGAPROPERTYMAPER映射器)
{
mapper.ConfigureMapping(message=>message.CustomerId)
.ToSaga(sagaData=>sagaData.CustomerId);
}
每次发送由saga处理的消息类型时,都需要配置
howtofindsaga()
方法已配置,因此它可以查找以前启动的saga以继续处理。因此本质上,您将为您发送的带有
StartProcess
消息的每个客户ID启动一个新的saga
。您可以在此处阅读更多信息:


因此,现在真正的问题是,您现在真的需要使用saga吗?该示例似乎只处理一种类型的消息,所以您真的需要保存CustomerId的状态吗?saga的开销在您的示例中不是必需的,我相信基于上面的示例,一个常规处理程序就可以了。

最近回答,但你需要确保你的行为在SagaperResistenceBehavior之后执行

在IConfigureHisendPoint实现中:

    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");
        }
    }
公共虚拟空间自定义(端点配置)
{
configuration.Pipeline.Register();
}
公共类注册:RegisterStep
{
公开注册()
:基本(
stepId:“审核变量”,
行为:typeof(AuditMutator),
说明:“为审核设置”)
{
本文件中的InsertAfterIfExists(“InvokeSaga”);
}
}

谢谢@ColinPear的回复。两件事1)我了解数据。CustomerId不会设置在那里……这就是我想看到的。原因是如果我们可以在管道中处理它,那么每个使用
IContainSagaData
的开发人员都会将其设置为“免费”.2)
ConfigureHowToFindSaga
在评论中提到。为了简洁起见,我删除了它,因为(我认为)它对解释问题并不重要。3)上面的代码示例不是真实的代码,而是为了可读性而高度精简的假场景。我们的故事好的。那么本质上你是在尝试填充customerId,而不必将其放在邮件上?目前,customerId位于邮件标题中的每条邮件上,我希望“自动”将其复制到saga数据中…我的意思是在管道中的某个地方。在管道中设置值是一种非常复杂的方法,可以避免在传奇中编写一行代码。尽管如此,我认为你无法实现你想要的目标,原因有两个:1)传奇目前不存在。它还没有被创建,那么传奇数据记录会是什么d到?2)如果它确实存在,您必须重写查找sagas的所有代码并将其放入管道中。您还需要在管道中包含如何查找它的业务逻辑(也称为ConfigureHowToFindSaga()),以及混合业务逻辑和基础结构代码。