diff --git a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs index 3ee4f90d268..e296543d0de 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs @@ -16,6 +16,9 @@ public partial class NotificationEntryComponent : ComponentBase [Parameter] public EventCallback OnDismiss { get; set; } + [Inject] + public required IServiceProvider Services { get; init; } + private string IntentClass => Entry.Intent switch { MessageIntent.Success => "intent-success", @@ -49,7 +52,7 @@ private async Task HandlePrimaryAction() { if (Entry.PrimaryAction is { } primaryAction) { - await primaryAction.OnClick(); + await primaryAction.OnClick(Services); } } } diff --git a/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs b/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs index 1273e86f56f..1712635cfb2 100644 --- a/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs +++ b/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs @@ -165,7 +165,7 @@ public async Task ExecuteAsyncCore(ResourceViewModel resource, CommandViewModel if (response.Result is not null) { toastParameters.PrimaryAction = loc[nameof(Dashboard.Resources.Resources.ResourceCommandViewResponse)]; - toastParameters.OnPrimaryAction = EventCallback.Factory.Create(this, () => OpenViewResponseDialogAsync(command, response)); + toastParameters.OnPrimaryAction = EventCallback.Factory.Create(this, () => OpenViewResponseDialogAsync(dialogService, command, response)); } notificationService.ReplaceNotification(progressNotificationId, new NotificationEntry @@ -173,12 +173,12 @@ public async Task ExecuteAsyncCore(ResourceViewModel resource, CommandViewModel Title = successTitle, Body = response.Message, Intent = MessageIntent.Success, - PrimaryAction = response.Result is not null ? CreateViewResponseNotificationAction(command, response) : null + PrimaryAction = response.Result is not null ? CreateViewResponseNotificationAction(loc, command, response) : null }); if (response.Result?.DisplayImmediately == true) { - await OpenViewResponseDialogAsync(command, response).ConfigureAwait(false); + await OpenViewResponseDialogAsync(dialogService, command, response).ConfigureAwait(false); } } else if (response.Kind == ResourceCommandResponseKind.Cancelled) @@ -206,7 +206,7 @@ public async Task ExecuteAsyncCore(ResourceViewModel resource, CommandViewModel if (response.Result is not null) { toastParameters.SecondaryAction = loc[nameof(Dashboard.Resources.Resources.ResourceCommandViewResponse)]; - toastParameters.OnSecondaryAction = EventCallback.Factory.Create(this, () => OpenViewResponseDialogAsync(command, response)); + toastParameters.OnSecondaryAction = EventCallback.Factory.Create(this, () => OpenViewResponseDialogAsync(dialogService, command, response)); } notificationService.ReplaceNotification(progressNotificationId, new NotificationEntry @@ -214,12 +214,12 @@ public async Task ExecuteAsyncCore(ResourceViewModel resource, CommandViewModel Title = failedTitle, Body = response.Message, Intent = MessageIntent.Error, - PrimaryAction = response.Result is not null ? CreateViewResponseNotificationAction(command, response) : null + PrimaryAction = response.Result is not null ? CreateViewResponseNotificationAction(loc, command, response) : null }); if (response.Result?.DisplayImmediately == true) { - await OpenViewResponseDialogAsync(command, response).ConfigureAwait(false); + await OpenViewResponseDialogAsync(dialogService, command, response).ConfigureAwait(false); } } @@ -261,16 +261,22 @@ private static (Icon Icon, Color Color)? GetIntentIcon(ToastIntent intent) }; } - private NotificationAction CreateViewResponseNotificationAction(CommandViewModel command, ResourceCommandResponseViewModel response) + private static NotificationAction CreateViewResponseNotificationAction(IStringLocalizer loc, CommandViewModel command, ResourceCommandResponseViewModel response) { return new NotificationAction { Text = loc[nameof(Dashboard.Resources.Resources.ResourceCommandViewResponse)], - OnClick = () => OpenViewResponseDialogAsync(command, response) + OnClick = (services) => + { + // Get dialog service from passed in services since this data is long lived. + // Using the dialog service from executor could cause closure over scoped services. + var dialogService = services.GetRequiredService(); + return OpenViewResponseDialogAsync(dialogService, command, response); + } }; } - private async Task OpenViewResponseDialogAsync(CommandViewModel command, ResourceCommandResponseViewModel response) + private static async Task OpenViewResponseDialogAsync(DashboardDialogService dialogService, CommandViewModel command, ResourceCommandResponseViewModel response) { var fixedFormat = response.Result!.Format switch { diff --git a/src/Aspire.Dashboard/Model/INotificationService.cs b/src/Aspire.Dashboard/Model/INotificationService.cs index 408b7d589df..f8f744ac766 100644 --- a/src/Aspire.Dashboard/Model/INotificationService.cs +++ b/src/Aspire.Dashboard/Model/INotificationService.cs @@ -72,7 +72,7 @@ public sealed class NotificationEntry public sealed class NotificationAction { public required string Text { get; init; } - public required Func OnClick { get; init; } + public required Func OnClick { get; init; } } ///