![]() |
VOOZH | about |
dotnet add package I-Synergy.Framework.AspNetCore.Blazor --version 2026.10618.11733
NuGet\Install-Package I-Synergy.Framework.AspNetCore.Blazor -Version 2026.10618.11733
<PackageReference Include="I-Synergy.Framework.AspNetCore.Blazor" Version="2026.10618.11733" />
<PackageVersion Include="I-Synergy.Framework.AspNetCore.Blazor" Version="2026.10618.11733" />Directory.Packages.props
<PackageReference Include="I-Synergy.Framework.AspNetCore.Blazor" />Project file
paket add I-Synergy.Framework.AspNetCore.Blazor --version 2026.10618.11733
#r "nuget: I-Synergy.Framework.AspNetCore.Blazor, 2026.10618.11733"
#:package I-Synergy.Framework.AspNetCore.Blazor@2026.10618.11733
#addin nuget:?package=I-Synergy.Framework.AspNetCore.Blazor&version=2026.10618.11733Install as a Cake Addin
#tool nuget:?package=I-Synergy.Framework.AspNetCore.Blazor&version=2026.10618.11733Install as a Cake Tool
Comprehensive Blazor component library and services for building modern ASP.NET Core Blazor applications. This package provides authentication providers, navigation menu services, exception handling, antiforgery protection, form factor detection, and static asset management for both Blazor Server and Blazor WebAssembly applications.
Install the package via NuGet:
dotnet add package I-Synergy.Framework.AspNetCore.Blazor
In your Program.cs:
using ISynergy.Framework.AspNetCore.Blazor.Extensions;
using ISynergy.Framework.Core.Abstractions;
using ISynergy.Framework.Core.Abstractions.Services;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Configure Blazor services with framework integration
builder.Services.ConfigureServices<AppContext, CommonServices, SettingsService, Resources>(
builder.Configuration,
infoService: new InfoService(),
action: services =>
{
// Add your custom services here
services.AddScoped<IDataService, DataService>();
},
assembly: typeof(Program).Assembly,
assemblyFilter: name => name.Name.StartsWith("YourApp"));
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
In your appsettings.json:
{
"AnalyticOptions": {
"MeasurementId": "G-XXXXXXXXXX",
"TrackingId": "UA-XXXXXXXXX-X"
},
"ClientApplicationOptions": {
"ApplicationName": "My Blazor App",
"Version": "1.0.0"
}
}
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Providers;
using Microsoft.AspNetCore.Components;
@inject IAuthenticationProvider AuthenticationProvider
@code {
private string username;
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity?.IsAuthenticated == true)
{
username = user.Identity.Name;
}
}
private async Task LoginAsync()
{
// Trigger login flow
await AuthenticationProvider.LoginAsync();
}
private async Task LogoutAsync()
{
// Trigger logout flow
await AuthenticationProvider.LogoutAsync();
}
}
Define your navigation structure:
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Services;
using ISynergy.Framework.AspNetCore.Blazor.Models;
using ISynergy.Framework.AspNetCore.Blazor.Services.Base;
public class MyNavigationMenuService : BaseNavigationMenuService
{
public MyNavigationMenuService()
{
MenuItems = new List<NavigationItem>
{
new NavigationLink
{
Name = "Home",
Href = "/",
Icon = "home",
Match = NavLinkMatch.All
},
new NavigationGroup
{
Name = "Products",
Icon = "shopping-cart",
Items = new List<NavigationItem>
{
new NavigationLink
{
Name = "All Products",
Href = "/products",
Icon = "list"
},
new NavigationLink
{
Name = "New Product",
Href = "/products/new",
Icon = "plus"
}
}
},
new NavigationGroup
{
Name = "Settings",
Icon = "settings",
Items = new List<NavigationItem>
{
new NavigationLink
{
Name = "Profile",
Href = "/settings/profile",
Icon = "user"
},
new NavigationLink
{
Name = "Security",
Href = "/settings/security",
Icon = "lock"
}
}
}
};
}
}
// Register in DI
builder.Services.AddScoped<INavigationMenuService, MyNavigationMenuService>();
using ISynergy.Framework.Core.Abstractions.Services;
using Microsoft.AspNetCore.Components;
@inject IExceptionHandlerService ExceptionHandler
@code {
private async Task LoadDataAsync()
{
try
{
var data = await DataService.GetDataAsync();
// Process data
}
catch (Exception ex)
{
// Let the exception handler manage the error display
await ExceptionHandler.HandleExceptionAsync(ex);
}
}
}
ISynergy.Framework.AspNetCore.Blazor.Services/
├── ExceptionHandlerService # Global exception handling with filtering
├── StaticAssetService # Load resources from wwwroot
├── FormFactorService # Device and screen size detection
└── BaseNavigationMenuService # Base class for navigation menus
ISynergy.Framework.AspNetCore.Blazor.Providers/
└── AuthenticationProvider # Authentication state management
ISynergy.Framework.AspNetCore.Blazor.Security/
└── AntiforgeryHttpClientFactory # CSRF token integration for HTTP calls
ISynergy.Framework.AspNetCore.Blazor.Models/
├── NavigationItem # Base navigation item
├── NavigationLink # Single navigation link
├── NavigationGroup # Grouped navigation items
└── CookieState # Cookie management
ISynergy.Framework.AspNetCore.Blazor.Components/
└── View # Base view component for MVVM
Protect your API calls with CSRF tokens:
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Security;
using Microsoft.AspNetCore.Components;
@inject IAntiforgeryHttpClientFactory HttpClientFactory
@code {
private async Task SaveDataAsync(DataModel data)
{
// Create client with antiforgery token automatically added
var client = await HttpClientFactory.CreateClientAsync("authorizedClient");
var response = await client.PostAsJsonAsync("/api/data", data);
if (response.IsSuccessStatusCode)
{
// Success
}
}
}
Configure the client in Program.cs:
builder.Services.AddHttpClient("authorizedClient", client =>
{
client.BaseAddress = new Uri(builder.Configuration["ApiBaseUrl"]);
});
Detect device types and screen sizes for responsive design:
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Services;
using Microsoft.AspNetCore.Components;
@inject IFormFactorService FormFactorService
@code {
private bool isMobile;
private bool isTablet;
private bool isDesktop;
protected override void OnInitialized()
{
isMobile = FormFactorService.IsMobile;
isTablet = FormFactorService.IsTablet;
isDesktop = FormFactorService.IsDesktop;
// Subscribe to form factor changes
FormFactorService.FormFactorChanged += OnFormFactorChanged;
}
private void OnFormFactorChanged(object sender, EventArgs e)
{
isMobile = FormFactorService.IsMobile;
isTablet = FormFactorService.IsTablet;
isDesktop = FormFactorService.IsDesktop;
StateHasChanged();
}
public void Dispose()
{
FormFactorService.FormFactorChanged -= OnFormFactorChanged;
}
}
<div class="@(isMobile ? "mobile-layout" : isTablet ? "tablet-layout" : "desktop-layout")">
@if (isMobile)
{
<MobileView />
}
else if (isTablet)
{
<TabletView />
}
else
{
<DesktopView />
}
</div>
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Services;
using Microsoft.AspNetCore.Components;
@inject IStaticAssetService StaticAssetService
@code {
private string logoData;
protected override async Task OnInitializedAsync()
{
// Load an image from wwwroot
var imageBytes = await StaticAssetService.GetAssetAsync("images/logo.png");
logoData = $"data:image/png;base64,{Convert.ToBase64String(imageBytes)}";
}
}
<img src="@logoData" alt="Logo" />
The exception handler automatically filters common exceptions:
using ISynergy.Framework.Core.Abstractions.Services;
public class DataService
{
private readonly IExceptionHandlerService _exceptionHandler;
public DataService(IExceptionHandlerService exceptionHandler)
{
_exceptionHandler = exceptionHandler;
}
public async Task LoadDataAsync(CancellationToken cancellationToken)
{
try
{
await LoadAsync(cancellationToken);
}
catch (Exception ex)
{
// The handler automatically filters:
// - TaskCanceledException when cancellation is requested
// - OperationCanceledException
// - WebSocketException
// - Duplicate error messages
// - I/O abort exceptions
await _exceptionHandler.HandleExceptionAsync(ex);
}
}
}
using ISynergy.Framework.AspNetCore.Blazor.Models;
public class AppNavigationMenuService : BaseNavigationMenuService
{
public AppNavigationMenuService()
{
MenuItems = new List<NavigationItem>
{
// Dashboard
new NavigationLink
{
Name = "Dashboard",
Href = "/",
Icon = "dashboard",
Match = NavLinkMatch.All
},
// Sales group with sub-items
new NavigationGroup
{
Name = "Sales",
Icon = "shopping-bag",
Items = new List<NavigationItem>
{
new NavigationLink
{
Name = "Orders",
Href = "/sales/orders",
Icon = "receipt"
},
new NavigationLink
{
Name = "Customers",
Href = "/sales/customers",
Icon = "people"
},
new NavigationLink
{
Name = "Products",
Href = "/sales/products",
Icon = "box"
}
}
},
// Reports group
new NavigationGroup
{
Name = "Reports",
Icon = "chart",
Items = new List<NavigationItem>
{
new NavigationLink
{
Name = "Sales Report",
Href = "/reports/sales",
Icon = "trending-up"
},
new NavigationLink
{
Name = "Customer Report",
Href = "/reports/customers",
Icon = "bar-chart"
}
}
},
// Settings
new NavigationLink
{
Name = "Settings",
Href = "/settings",
Icon = "settings"
}
};
// FlattenedMenuItems is automatically populated
// Contains all links without groups for search functionality
}
}
@inject INavigationMenuService NavigationMenuService
<nav class="sidebar">
@foreach (var item in NavigationMenuService.MenuItems)
{
@if (item is NavigationLink link)
{
<NavLink href="@link.Href" Match="@link.Match" class="nav-link">
<i class="icon icon-@link.Icon"></i>
<span>@link.Name</span>
</NavLink>
}
else if (item is NavigationGroup group)
{
<div class="nav-group">
<div class="nav-group-header">
<i class="icon icon-@group.Icon"></i>
<span>@group.Name</span>
</div>
<div class="nav-group-items">
@foreach (var subItem in group.Items)
{
@if (subItem is NavigationLink subLink)
{
<NavLink href="@subLink.Href" Match="@subLink.Match" class="nav-link">
<i class="icon icon-@subLink.Icon"></i>
<span>@subLink.Name</span>
</NavLink>
}
}
</div>
</div>
}
}
</nav>
using ISynergy.Framework.Core.Abstractions.Services;
using ISynergy.Framework.Core.Constants;
using ISynergy.Framework.Mvvm.Messages;
public class CustomExceptionHandlerService : ExceptionHandlerService
{
public CustomExceptionHandlerService(
IBusyService busyService,
ILogger<CustomExceptionHandlerService> logger)
: base(busyService, logger)
{
}
public override Task HandleExceptionAsync(Exception exception)
{
// Handle custom exception types
if (exception is ValidationException validationException)
{
MessengerService.Default.Send(new ShowWarningMessage(
new MessageBoxRequest(validationException.Message)));
return Task.CompletedTask;
}
if (exception is BusinessRuleException businessException)
{
MessengerService.Default.Send(new ShowInformationMessage(
new MessageBoxRequest(businessException.Message)));
return Task.CompletedTask;
}
// Fall back to base implementation
return base.HandleExceptionAsync(exception);
}
}
// Register custom handler
builder.Services.AddSingleton<IExceptionHandlerService, CustomExceptionHandlerService>();
@implements IDisposable
@inject IFormFactorService FormFactorService
<div class="layout @GetLayoutClass()">
@if (FormFactorService.IsMobile)
{
<div class="mobile-header">
<button @onclick="ToggleMenu">Menu</button>
<h1>@Title</h1>
</div>
@if (showMenu)
{
<div class="mobile-menu">
<NavigationMenu />
</div>
}
<div class="mobile-content">
@ChildContent
</div>
}
else
{
<div class="sidebar">
<NavigationMenu />
</div>
<div class="main-content">
<header>
<h1>@Title</h1>
</header>
<main>
@ChildContent
</main>
</div>
}
</div>
@code {
[Parameter]
public string Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private bool showMenu = false;
protected override void OnInitialized()
{
FormFactorService.FormFactorChanged += OnFormFactorChanged;
}
private void OnFormFactorChanged(object sender, EventArgs e)
{
StateHasChanged();
}
private string GetLayoutClass()
{
if (FormFactorService.IsMobile) return "mobile";
if (FormFactorService.IsTablet) return "tablet";
return "desktop";
}
private void ToggleMenu()
{
showMenu = !showMenu;
}
public void Dispose()
{
FormFactorService.FormFactorChanged -= OnFormFactorChanged;
}
}
Use the INavigationMenuService to centralize your menu structure and make it easy to update across your application.
Always use IAntiforgeryHttpClientFactory for POST/PUT/DELETE operations to prevent CSRF attacks.
The ExceptionHandlerService automatically filters common exceptions. Extend it for custom exception types.
Example unit tests for Blazor components:
using Bunit;
using ISynergy.Framework.AspNetCore.Blazor.Abstractions.Services;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
public class NavigationMenuTests : TestContext
{
[Fact]
public void NavigationMenu_RendersAllItems()
{
// Arrange
var menuService = new Mock<INavigationMenuService>();
menuService.Setup(m => m.MenuItems).Returns(new List<NavigationItem>
{
new NavigationLink { Name = "Home", Href = "/" },
new NavigationLink { Name = "About", Href = "/about" }
});
Services.AddSingleton(menuService.Object);
// Act
var cut = RenderComponent<NavigationMenu>();
// Assert
cut.Find("a[href='/']").TextContent.Should().Contain("Home");
cut.Find("a[href='/about']").TextContent.Should().Contain("About");
}
[Fact]
public async Task ExceptionHandler_FiltersTaskCanceledException()
{
// Arrange
var busyService = new Mock<IBusyService>();
var logger = Mock.Of<ILogger<ExceptionHandlerService>>();
var handler = new ExceptionHandlerService(busyService.Object, logger);
var cts = new CancellationTokenSource();
cts.Cancel();
var exception = new TaskCanceledException("Test", null, cts.Token);
// Act
await handler.HandleExceptionAsync(exception);
// Assert - no message should be shown for cancelled tasks
busyService.Verify(b => b.StopBusy(), Times.Never);
}
}
For more information about the I-Synergy Framework:
For issues, questions, or contributions, please visit the GitHub repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.10618.11733 | 0 | 6/18/2026 |
| 2026.10618.11702-preview | 0 | 6/18/2026 |
| 2026.10616.12121 | 40 | 6/16/2026 |
| 2026.10616.11904-preview | 39 | 6/16/2026 |
| 2026.10616.10010 | 59 | 6/15/2026 |
| 2026.10615.12240-preview | 74 | 6/15/2026 |
| 2026.10615.10047-preview | 88 | 6/14/2026 |
| 2026.10614.10112-preview | 92 | 6/13/2026 |
| 2026.10612.12341-preview | 97 | 6/12/2026 |
| 2026.10612.12110-preview | 80 | 6/12/2026 |
| 2026.10612.11941-preview | 82 | 6/12/2026 |
| 2026.10610.10831 | 90 | 6/10/2026 |
| 2026.10610.10706-preview-pr... | 84 | 6/10/2026 |
| 2026.10609.11323-preview | 75 | 6/9/2026 |
| 2026.10607.11905-preview | 87 | 6/7/2026 |
| 2026.10607.11454-preview | 100 | 6/7/2026 |
| 2026.10606.11854-preview | 88 | 6/6/2026 |
| 2026.10603.11238-preview | 88 | 6/3/2026 |
| 2026.10602.12203-preview | 90 | 6/2/2026 |
| 2026.10602.11949-preview | 89 | 6/2/2026 |