Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,52 @@ public partial class FeedbackPage : AppPageBase
}
```

## Owned services

### Default Service Lifetime in Blazor Components

By default, services injected in Blazor components remain tied to the application scope for the entire lifetime:

- **Blazor Server**: Until the user closes the browser tab or the browser gets disconnected.
- **Blazor WebAssembly / Blazor Hybrid**: Until the browser tab or app is closed.

This is perfectly fine for most services (especially singletons or stateless ones), but services that hold resources (timers, event subscriptions, native handlers, etc.) may need to be disposed when their associated component is destroyed.

### Using ScopedServices for Automatic Disposal

To achieve automatic disposal when the component is disposed, inject the service via `ScopedServices` instead of using `[AutoInject]`. This creates a scoped service instance that gets disposed along with the component.

**Example:**

```csharp
Keyboard keyboard => field ??= ScopedServices.GetRequiredService<Keyboard>(); // ??= means the service gets resolved when accessed, results into better performance.

protected override async Task OnAfterFirstRenderAsync()
{
await keyboard.Add(ButilKeyCodes.KeyF, () => searchBox.FocusAsync(), ButilModifiers.Ctrl); // Handles keyboard shortcuts

await base.OnAfterFirstRenderAsync();
}
```

Instead of

```csharp
[AutoInject] private Keyboard keyboard = default!;

protected override async Task OnAfterFirstRenderAsync()
{
await keyboard.Add(ButilKeyCodes.KeyF, () => searchBox.FocusAsync(), ButilModifiers.Ctrl); // Handles keyboard shortcuts

await base.OnAfterFirstRenderAsync();
}

protected override async ValueTask DisposeAsync(bool disposing)
{
await keyboard.DisposeAsync();
await base.DisposeAsync(disposing);
}
```
---

### AI Wiki: Answered Questions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ public partial class AppClientCoordinator : AppComponentBase
[AutoInject] private UserAgent userAgent = default!;
[AutoInject] private IJSRuntime jsRuntime = default!;
[AutoInject] private IUserController userController = default!;
[AutoInject] private IStorageService storageService = default!;
[AutoInject] private ILogger<AuthManager> authLogger = default!;
[AutoInject] private ILogger<Navigator> navigatorLogger = default!;
[AutoInject] private ILogger<AppClientCoordinator> logger = default!;
[AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!;
//#if (notification == true)
[AutoInject] private IPushNotificationService pushNotificationService = default!;
//#endif
Expand Down Expand Up @@ -362,7 +360,7 @@ private async Task ConfigureUISetup()
if (CultureInfoManager.InvariantGlobalization is false)
{
CultureInfoManager.SetCurrentCulture(new Uri(NavigationManager.Uri).GetCulture() ?? // 1- Culture query string OR Route data request culture
await storageService.GetItem("Culture") ?? // 2- User settings
await StorageService.GetItem("Culture") ?? // 2- User settings
CultureInfo.CurrentUICulture.Name); // 3- OS settings
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ namespace Boilerplate.Client.Core.Components.Pages;

public partial class TodoPage
{
[AutoInject] Keyboard keyboard = default!;
[AutoInject] ITodoItemController todoItemController = default!;

// Refer to .docs/09- Dependency Injection & Service Registration.md 's Owned services section for more information about ScopedServices
Keyboard keyboard => field ??= ScopedServices.GetRequiredService<Keyboard>();

private bool isLoading;
private string? searchText;
private string? selectedSort;
Expand Down Expand Up @@ -193,14 +195,4 @@ private async Task UpdateTodoItem(TodoItemDto todoItem)
viewTodoItems.Remove(todoItem);
}
}

protected override async ValueTask DisposeAsync(bool disposing)
{
await base.DisposeAsync(true);

if (disposing)
{
await keyboard.DisposeAsync();
}
}
}
Loading