-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Labels
Description
Describe the feature.
Functions counterpart to Particular/NServiceBus#6973
Is your feature related to a problem? Please describe.
With function the whole story becomes even more complex. Because we do not bind IMessageSession and IFunctionEndpoint require access to FunctionContext the user needs to go through all sorts of hurdles to be able to send a message within recoverability. The code looks more or less like that
public interface IFunctionContextAccessor
{
FunctionContext? FunctionContext { get; set; }
}
internal sealed class FunctionContextAccessor : IFunctionContextAccessor
{
private AsyncLocal<FunctionContextHolder> currentContext = new();
public FunctionContext? FunctionContext
{
get => currentContext.Value?.FunctionContext;
set
{
var holder = currentContext.Value;
if (holder != null)
{
holder.FunctionContext = null;
}
if (value != null)
{
currentContext.Value = new FunctionContextHolder { FunctionContext = value };
}
}
}
private class FunctionContextHolder
{
public FunctionContext? FunctionContext;
}
}public sealed class FunctionContextAccessorMiddleware : IFunctionsWorkerMiddleware
{
private IFunctionContextAccessor FunctionContextAccessor { get; }
public FunctionContextAccessorMiddleware(IFunctionContextAccessor accessor)
{
FunctionContextAccessor = accessor;
}
public Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
if (FunctionContextAccessor.FunctionContext != null)
{
throw new InvalidOperationException($"Unable to initalize {nameof(IFunctionContextAccessor)}: context has already been initialized.");
}
FunctionContextAccessor.FunctionContext = context;
return next(context);
}
}class RecoverabilityBehavior(IFunctionEndpoint functionEndpoint, IFunctionContextAccessor contextAccessor, ILogger<RecoverabilityBehavior> logger) : Behavior<IRecoverabilityContext>
{
public override async Task Invoke(IRecoverabilityContext context, Func<Task> next)
{
if (context.RecoverabilityAction is MoveToError && context.Metadata.TryGetValue("NServiceBus.ExceptionInfo.Data.Message type", out var messageType) && messageType.EndsWith(nameof(MyMessage)))
{
logger.LogInformation("MyMessage failed. Publishing MyMessageFailed event.");
var message = new MyMessageFailed();
await functionEndpoint.Publish(message, contextAccessor.FunctionContext, context.CancellationToken);
}
await next();
}
} var host = new HostBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<RecoverabilityBehavior>();
services.AddSingleton<IFunctionContextAccessor, FunctionContextAccessor>();
})
.ConfigureFunctionsWorkerDefaults(app =>
{
app.UseMiddleware<FunctionContextAccessorMiddleware>();
})
.UseNServiceBus(endpointConfiguration =>
{
endpointConfiguration.AdvancedConfiguration.Pipeline.Register(p => p.GetRequiredService<RecoverabilityBehavior>(), "Publishes an event in certain cases.");
})
.Build();Describe the requested feature
Plus now the Core approach is no longer interoperable with the functions approach due to the lack of a common way to send messages.
Describe alternatives you've considered
Additional Context
No response
Reactions are currently unavailable