Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
public class AutoInjectSourceGenerator : ISourceGenerator
{
private static int counter;
private static readonly DiagnosticDescriptor NonPartialClassError = new DiagnosticDescriptor(id: "BITGEN001",

Check warning on line 17 in src/SourceGenerators/Bit.SourceGenerators/AutoInject/AutoInjectSourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build Bit.SourceGenerators

Enable analyzer release tracking for the analyzer project containing rule 'BITGEN001' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 17 in src/SourceGenerators/Bit.SourceGenerators/AutoInject/AutoInjectSourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build Bit.SourceGenerators

Enable analyzer release tracking for the analyzer project containing rule 'BITGEN001' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
title: "The class needs to be partial",
messageFormat: "{0} is not partial. The AutoInject attribute needs to be used only in partial classes.",
category: "Bit.SourceGenerators",
Expand Down Expand Up @@ -82,48 +82,6 @@

private static string? GenerateSource(INamedTypeSymbol? attributeSymbol, INamedTypeSymbol? classSymbol, IReadOnlyCollection<ISymbol> eligibleMembers)
{
AutoInjectClassType env = FigureOutTypeOfEnvironment(classSymbol);
return env switch
{
AutoInjectClassType.NormalClass => AutoInjectNormalClassHandler.Generate(attributeSymbol, classSymbol, eligibleMembers),
AutoInjectClassType.RazorComponent => AutoInjectRazorComponentHandler.Generate(classSymbol, eligibleMembers),
_ => string.Empty
};
}

private static AutoInjectClassType FigureOutTypeOfEnvironment(INamedTypeSymbol? @class)
{
if (@class is null)
throw new ArgumentNullException(nameof(@class));

if (IsClassIsRazorComponent(@class))
return AutoInjectClassType.RazorComponent;
else
return AutoInjectClassType.NormalClass;
}

private static bool IsClassIsRazorComponent(INamedTypeSymbol @class)
{
bool isInheritIComponent = @class.AllInterfaces.Any(o => o.ToDisplayString() == "Microsoft.AspNetCore.Components.IComponent");

if (isInheritIComponent)
return true;

var classFilePaths = @class.Locations
.Where(o => o.SourceTree is not null)
.Select(o => o.SourceTree?.FilePath)
.ToList();

string razorFileName = $"{@class.Name}.razor";

foreach (var path in classFilePaths)
{
string directoryPath = Path.GetDirectoryName(path) ?? string.Empty;
string filePath = Path.Combine(directoryPath, razorFileName);
if (File.Exists(filePath))
return true;
}

return false;
return AutoInjectNormalClassHandler.Generate(attributeSymbol, classSymbol, eligibleMembers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,38 @@ 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>();
```

Insetad of
Comment thread
yasmoradi marked this conversation as resolved.
Outdated

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

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 @@ -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