diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4f5926261..5fb15e4e5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,7 +14,7 @@ name: "CodeQL" on: push: branches: [ "main" ] - aths-ignore: + paths-ignore: - "deploy/**" pull_request: # The branches below must be a subset of the branches above @@ -33,43 +33,35 @@ jobs: security-events: write pull-requests: read + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] steps: - name: Checkout repository uses: actions/checkout@v4 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: csharp - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - # - name: Autobuild - # uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x + include-prerelease: true + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + language: ${{ matrix.language }} + queries: security-and-quality - name: Restore dependencies - run: dotnet restore CleanArchitecture.Blazor.slnx + run: dotnet restore CleanArchitecture.Blazor.slnx + - name: Build - run: dotnet build CleanArchitecture.Blazor.slnx --configuration Debug --no-restore + run: dotnet build CleanArchitecture.Blazor.slnx --configuration Release --no-restore - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - languages: csharp \ No newline at end of file + # - name: Perform CodeQL Analysis + # uses: github/codeql-action/analyze@v4 + # with: + # category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f825b08eb..a9fa2af70 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -19,11 +19,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Restore dependencies run: dotnet restore CleanArchitecture.Blazor.slnx diff --git a/CleanArchitecture.Blazor.nuspec b/CleanArchitecture.Blazor.nuspec index db560fa76..c1f259e90 100644 --- a/CleanArchitecture.Blazor.nuspec +++ b/CleanArchitecture.Blazor.nuspec @@ -2,25 +2,35 @@ CleanArchitecture.Blazor.Solution.Template - 1.30.2 + 1.30.3 Clean Architecture Blazor Solution Template hl.z - Clean Architecture Blazor Solution Template for .NET9. + Clean Architecture Blazor Solution Template for .NET 10. - A Clean Architecture Blazor Server dashboard application template for .NET9. + A Clean Architecture Blazor Server dashboard application template for .NET 10. https://github.com/neozhu/CleanArchitectureWithBlazorServer diff --git a/Dockerfile b/Dockerfile index ae994c76e..9da65cde4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base # apt update and install fonts RUN echo "deb http://deb.debian.org/debian/ bookworm main contrib" > /etc/apt/sources.list && \ echo "deb-src http://deb.debian.org/debian/ bookworm main contrib" >> /etc/apt/sources.list && \ @@ -33,7 +33,7 @@ EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj", "src/Migrators/Migrators.MSSQL/"] @@ -60,4 +60,4 @@ FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "CleanArchitecture.Blazor.Server.UI.dll"] +ENTRYPOINT ["dotnet", "CleanArchitecture.Blazor.Server.UI.dll"] \ No newline at end of file diff --git a/README.md b/README.md index af02d04fc..4bd2a0425 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Clean Architecture Blazor Server +# Clean Architecture Blazor Server [![Build](https://github.com/neozhu/CleanArchitectureWithBlazorServer/actions/workflows/dotnet.yml/badge.svg)](https://github.com/neozhu/CleanArchitectureWithBlazorServer/actions/workflows/dotnet.yml) [![CodeQL](https://github.com/neozhu/CleanArchitectureWithBlazorServer/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/neozhu/CleanArchitectureWithBlazorServer/actions/workflows/codeql-analysis.yml) @@ -10,7 +10,7 @@ ## 🎯 Overview -This project is a production-ready Blazor Server application template that demonstrates Clean Architecture implementation with .NET 9 and follows **Blazor Clean Architecture best practices**. +This project is a production-ready Blazor Server application template that demonstrates Clean Architecture implementation with **.NET 10** and follows **Blazor Clean Architecture best practices**. It provides a solid foundation for building scalable, maintainable enterprise applications with modern **spec-driven development practices**. ### Key Features @@ -46,7 +46,7 @@ Experience the application in action: | Layer | Technologies | |-------|-------------| | **Frontend** | Blazor Server, MudBlazor, SignalR | -| **Backend** | .NET 9, ASP.NET Core, MediatR, FluentValidation | +| **Backend** | .NET 10, ASP.NET Core, MediatR, FluentValidation | | **Database** | Entity Framework Core, MSSQL/PostgreSQL/SQLite | | **Authentication** | ASP.NET Core Identity, OAuth 2.0, JWT | | **Caching** | FusionCache, Redis | @@ -89,7 +89,7 @@ The project includes a comprehensive [Development Workflow](docs/) with: ### Prerequisites -- [.NET 9 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) +- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) - [Visual Studio 2022](https://visualstudio.microsoft.com/) or [Rider](https://www.jetbrains.com/rider/) - [Docker Desktop](https://www.docker.com/) (optional) @@ -150,7 +150,7 @@ See [Docker Setup Documentation](#docker-setup-for-blazor-server-application) fo OpenSpec enables spec-driven, reviewable changes with clear proposals, deltas, and tasks. This repo includes guidance in `openspec/AGENTS.md` and a project context in `openspec/project.md`. - Read the quickstart: `openspec/AGENTS.md` -- Project conventions and patterns: `openspec/project.md` (see “New Entity/Feature Guide (Contacts Pattern)”) +- Project conventions and patterns: `openspec/project.md` (see "New Entity/Feature Guide (Contacts Pattern)") ### Workflow @@ -181,7 +181,7 @@ OpenSpec enables spec-driven, reviewable changes with clear proposals, deltas, a - For data access in handlers use `IApplicationDbContextFactory` and per-operation context lifetime: - `await using var db = await _dbContextFactory.CreateAsync(cancellationToken);` - Follow MediatR pipeline behaviors, caching tags, and specification patterns. - - Mirror the Contacts module for a new entity’s DTOs, commands, queries, specs, security, and UI pages/components. + - Mirror the Contacts module for a new entity's DTOs, commands, queries, specs, security, and UI pages/components. 5) Archive after deployment - Move `openspec/changes//` to `openspec/changes/archive/YYYY-MM-DD-/` (or use the CLI archive helper if available). @@ -236,7 +236,7 @@ The system SHALL allow users to create, edit, view, list, and delete customers w Tips - Use Contacts as the reference implementation for structure and conventions. - Add menu entries in `src/Server.UI/Services/Navigation/MenuService.cs`. -- Define permissions under `Permissions.` and they’ll be picked up during seeding. +- Define permissions under `Permissions.` and they'll be picked up during seeding. ## 🔧 Code Generation diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index b06b3a482..0c5521920 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -1,6 +1,6 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Application CleanArchitecture.Blazor.Application enable @@ -21,10 +21,10 @@ - - + + - + @@ -34,4 +34,4 @@ - + \ No newline at end of file diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index dd9210314..e696d2e2d 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Domain CleanArchitecture.Blazor.Domain enable @@ -11,23 +11,23 @@ - - + + - - - - - - - - + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + \ No newline at end of file diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index c0bb635e0..162ad54cf 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -1,6 +1,6 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Infrastructure CleanArchitecture.Blazor.Infrastructure enable @@ -9,12 +9,12 @@ - - - - + + + + - + diff --git a/src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj b/src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj index 07a5bf088..1de49d723 100644 --- a/src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj +++ b/src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 enable enable CleanArchitecture.Blazor.Migrators.MSSQL @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/Migrators/Migrators.PostgreSQL/Migrators.PostgreSQL.csproj b/src/Migrators/Migrators.PostgreSQL/Migrators.PostgreSQL.csproj index c9455294e..909a9b5d9 100644 --- a/src/Migrators/Migrators.PostgreSQL/Migrators.PostgreSQL.csproj +++ b/src/Migrators/Migrators.PostgreSQL/Migrators.PostgreSQL.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 enable enable CleanArchitecture.Blazor.Migrators.PostgreSQL @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/Migrators/Migrators.SqLite/Migrators.SqLite.csproj b/src/Migrators/Migrators.SqLite/Migrators.SqLite.csproj index 3bcb937d7..4038b6bdf 100644 --- a/src/Migrators/Migrators.SqLite/Migrators.SqLite.csproj +++ b/src/Migrators/Migrators.SqLite/Migrators.SqLite.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 enable enable CleanArchitecture.Blazor.Migrators.SqLite @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/Server.UI/Components/Autocompletes/PickUserAutocomplete.razor.cs b/src/Server.UI/Components/Autocompletes/PickUserAutocomplete.razor.cs index 2f486a0f5..63e62e444 100644 --- a/src/Server.UI/Components/Autocompletes/PickUserAutocomplete.razor.cs +++ b/src/Server.UI/Components/Autocompletes/PickUserAutocomplete.razor.cs @@ -1,5 +1,4 @@ using CleanArchitecture.Blazor.Application.Features.Identity.DTOs; -using CleanArchitecture.Blazor.Application.Common.Interfaces; // IDataSourceService namespace CleanArchitecture.Blazor.Server.UI.Components.Autocompletes; diff --git a/src/Server.UI/Components/Autocompletes/PicklistAutocomplete.razor.cs b/src/Server.UI/Components/Autocompletes/PicklistAutocomplete.razor.cs index fb38f323e..d7fee9fbe 100644 --- a/src/Server.UI/Components/Autocompletes/PicklistAutocomplete.razor.cs +++ b/src/Server.UI/Components/Autocompletes/PicklistAutocomplete.razor.cs @@ -11,10 +11,12 @@ public PicklistAutocomplete() Clearable = true; Dense = true; ResetValueOnEmptyText = true; - + } - [Parameter] public Picklist Picklist { get; set; } + [Parameter] + [EditorRequired] + public Picklist Picklist { get; set; } [Inject] private IDataSourceService PicklistService { get; set; } = default!; @@ -30,7 +32,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await PicklistService.InitializeAsync(); } } - + private async Task PicklistService_OnChange() { @@ -43,31 +45,22 @@ protected override async ValueTask DisposeAsyncCore() await base.DisposeAsyncCore(); } - private async Task> SearchFunc_(string? value, CancellationToken cancellation = default) + private async Task> SearchFunc_(string? value, CancellationToken cancellation = default) { // if text is null or empty, show complete list - var term = value?.Trim(); - + var term = value?.Trim()??string.Empty; Expression> predicate; - - if (string.IsNullOrEmpty(term)) - { - predicate = x => x.Name == Picklist; - } - else - { - predicate = x => - x.Name == Picklist && - ( - (x.Value != null && x.Value.Contains(term)) || - (x.Text != null && x.Text.Contains(term)) - ); - } + predicate = x => + x.Name == Picklist && + ( + (x.Value != null && x.Value.Contains(term)) || + (x.Text != null && x.Text.Contains(term)) + ); var limit = MaxItems > 0 ? MaxItems : null; var results = await PicklistService.SearchAsync(predicate, limit, cancellation); return results.Select(x => x.Value ?? string.Empty); } - + } diff --git a/src/Server.UI/DependencyInjection.cs b/src/Server.UI/DependencyInjection.cs index 219bb7da3..e89d6d21e 100644 --- a/src/Server.UI/DependencyInjection.cs +++ b/src/Server.UI/DependencyInjection.cs @@ -44,9 +44,7 @@ public static IServiceCollection AddServerUI(this IServiceCollection services, I services.AddCascadingAuthenticationState(); services.AddMudServices(config => { - MudGlobal.InputDefaults.ShrinkLabel = true; - //MudGlobal.InputDefaults.Variant = Variant.Outlined; - //MudGlobal.ButtonDefaults.Variant = Variant.Outlined; + config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter; config.SnackbarConfiguration.NewestOnTop = false; config.SnackbarConfiguration.ShowCloseIcon = true; @@ -54,14 +52,8 @@ public static IServiceCollection AddServerUI(this IServiceCollection services, I config.SnackbarConfiguration.HideTransitionDuration = 500; config.SnackbarConfiguration.ShowTransitionDuration = 500; config.SnackbarConfiguration.SnackbarVariant = Variant.Filled; - - // we're currently planning on deprecating `PreventDuplicates`, at least to the end dev. however, - // we may end up wanting to instead set it as internal because the docs project relies on it - // to ensure that the Snackbar always allows duplicates. disabling the warning for now because - // the project is set to treat warnings as errors. -#pragma warning disable 0618 config.SnackbarConfiguration.PreventDuplicates = false; -#pragma warning restore 0618 + }); services.AddMudPopoverService(); services.AddMudBlazorSnackbar(); diff --git a/src/Server.UI/Pages/Contacts/Components/ContactFormDialog.razor b/src/Server.UI/Pages/Contacts/Components/ContactFormDialog.razor index 89d3422d3..9962e8a82 100644 --- a/src/Server.UI/Pages/Contacts/Components/ContactFormDialog.razor +++ b/src/Server.UI/Pages/Contacts/Components/ContactFormDialog.razor @@ -5,88 +5,63 @@ @inject IStringLocalizer L - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - @AppStrings.Cancel + + + + + @AppStrings.Cancel @AppStrings.Save - + @code { - MudForm _contactForm = new(); - private bool _saving = false; - private bool _savingnew = false; - [CascadingParameter] - IMudDialogInstance MudDialog { get; set; } = default!; - [EditorRequired] [Parameter] public AddEditContactCommand _model { get; set; } = null!; - async Task OnSubmit() - { - try - { - _saving = true; - await _contactForm.Validate().ConfigureAwait(false); - if (!_contactForm.IsValid) - return; - var result = await Mediator.Send(_model); - result.Match(data => - { - MudDialog.Close(DialogResult.Ok(true)); - Snackbar.Add(AppStrings.SaveSuccess, MudBlazor.Severity.Info); - }, errors => - { - Snackbar.Add(errors, MudBlazor.Severity.Error); - }); - } - finally - { - _saving = false; - } - } - async Task OnSaveAndNew() - { - try - { - _savingnew = true; - await _contactForm.Validate().ConfigureAwait(false); - if (!_contactForm.IsValid) - return; - var result = await Mediator.Send(_model); - await result.MatchAsync(async data => - { - Snackbar.Add(AppStrings.SaveSuccess, MudBlazor.Severity.Info); - await Task.Delay(300); - _model = new AddEditContactCommand() { }; - }, errors => - { - Snackbar.Add(errors, MudBlazor.Severity.Error); - return Task.CompletedTask; - }); - } - finally - { - _savingnew = false; - } - } - void Cancel() => MudDialog.Cancel(); + MudForm _contactForm = new(); + private bool _saving = false; + [CascadingParameter] + IMudDialogInstance MudDialog { get; set; } = default!; + [EditorRequired][Parameter] public AddEditContactCommand _model { get; set; } = null!; + async Task OnSubmit() + { + try + { + _saving = true; + await _contactForm.Validate().ConfigureAwait(false); + if (!_contactForm.IsValid) + return; + var result = await Mediator.Send(_model); + result.Match(data => + { + MudDialog.Close(DialogResult.Ok(true)); + Snackbar.Add(AppStrings.SaveSuccess, MudBlazor.Severity.Info); + }, errors => + { + Snackbar.Add(errors, MudBlazor.Severity.Error); + }); + } + finally + { + _saving = false; + } + } + + void Cancel() => MudDialog.Cancel(); } diff --git a/src/Server.UI/Pages/Identity/Login/LoginWith2fa.razor b/src/Server.UI/Pages/Identity/Login/LoginWith2fa.razor index bf05c9969..2d3986058 100644 --- a/src/Server.UI/Pages/Identity/Login/LoginWith2fa.razor +++ b/src/Server.UI/Pages/Identity/Login/LoginWith2fa.razor @@ -1,4 +1,4 @@ -@page "/account/loginwith2fa" +@page "/account/loginwith2fa" @using CleanArchitecture.Blazor.Domain.Identity @using System.ComponentModel.DataAnnotations @using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Login diff --git a/src/Server.UI/Server.UI.csproj b/src/Server.UI/Server.UI.csproj index e56d653d0..a4cdf96aa 100644 --- a/src/Server.UI/Server.UI.csproj +++ b/src/Server.UI/Server.UI.csproj @@ -1,9 +1,9 @@ - + CleanArchitecture.Blazor.Server.UI CleanArchitecture.Blazor.Server.UI - net9.0 + net10.0 enable $(NoWarn);VSTHRD200 enable @@ -22,12 +22,12 @@ - + - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Server.UI/appsettings.json b/src/Server.UI/appsettings.json index 8cef9e385..a4cbfed3c 100644 --- a/src/Server.UI/appsettings.json +++ b/src/Server.UI/appsettings.json @@ -1,4 +1,4 @@ -{ +{ "DatabaseSettings": { "DBProvider": "mssql", "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=BlazorDashboardDb;Trusted_Connection=True;MultipleActiveResultSets=true;" @@ -26,7 +26,7 @@ }, "AppConfigurationSettings": { "ApplicationUrl": "https://architecture.blazorserver.com", - "Version": "1.15", + "Version": "1.17", "App": "Blazor", "AppName": "Blazor Studio", "Company": "Company", @@ -75,7 +75,7 @@ "Properties": { "Application": "BlazorApp", "Environment": "Development", - "TargetFramework": "net9" + "TargetFramework": "net10" } }, @@ -105,4 +105,4 @@ "CriticalRiskThreshold": 80, "CacheExpirationMinutes": 15 } -} +} \ No newline at end of file diff --git a/tests/Application.IntegrationTests/Application.IntegrationTests.csproj b/tests/Application.IntegrationTests/Application.IntegrationTests.csproj index 6485e1800..aedbb2233 100644 --- a/tests/Application.IntegrationTests/Application.IntegrationTests.csproj +++ b/tests/Application.IntegrationTests/Application.IntegrationTests.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Application.IntegrationTests CleanArchitecture.Blazor.Application.IntegrationTests @@ -23,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -36,4 +36,4 @@ - + \ No newline at end of file diff --git a/tests/Application.UnitTests/Application.UnitTests.csproj b/tests/Application.UnitTests/Application.UnitTests.csproj index 42808552a..9f831f693 100644 --- a/tests/Application.UnitTests/Application.UnitTests.csproj +++ b/tests/Application.UnitTests/Application.UnitTests.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Application.UnitTests CleanArchitecture.Blazor.Application.UnitTests @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -26,4 +26,4 @@ - + \ No newline at end of file diff --git a/tests/Domain.UnitTests/Domain.UnitTests.csproj b/tests/Domain.UnitTests/Domain.UnitTests.csproj index e8d5d8f68..f20f27e45 100644 --- a/tests/Domain.UnitTests/Domain.UnitTests.csproj +++ b/tests/Domain.UnitTests/Domain.UnitTests.csproj @@ -1,7 +1,7 @@ - + - net9.0 + net10.0 CleanArchitecture.Blazor.Domain.UnitTests CleanArchitecture.Blazor.Domain.UnitTests @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -25,4 +25,4 @@ - + \ No newline at end of file