Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : upgrade to .NET 9 #1061

Merged
merged 6 commits into from
Nov 22, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/blazor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.108
dotnet-version: 9.x
- name: restore dependencies
run: dotnet restore ./src/apps/blazor/client/Client.csproj
- name: build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/webapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.108
dotnet-version: 9.x
- name: restore dependencies
run: dotnet restore ./src/api/server/Server.csproj
- name: build
Expand Down
2 changes: 1 addition & 1 deletion .template.config/template.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"identity": "FullStackHero.NET.StarterKit",
"name": "FullStackHero .NET Starter Kit",
"description": "The best way to start a full-stack .NET 8 Web App.",
"description": "The best way to start a full-stack .NET 9 Web App.",
"shortName": "fsh",
"sourceName": "FSH.Starter",
"preferNameDirectory": true,
Expand Down
4 changes: 2 additions & 2 deletions FSH.StarterKit.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<metadata>
<id>FullStackHero.NET.StarterKit</id>
<title>FullStackHero .NET Starter Kit</title>
<version>2.0.2-rc</version>
<version>2.0.3-rc</version>
<authors>Mukesh Murugan</authors>
<description>The best way to start a full-stack Multi-tenant .NET 8 Web App.</description>
<description>The best way to start a full-stack Multi-tenant .NET 9 Web App.</description>
<language>en-US</language>
<license type="file">./content/LICENSE</license>
<copyright>2024</copyright>
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# FullStackHero .NET 8 Starter Kit 🚀
# FullStackHero .NET 9 Starter Kit 🚀

> With ASP.NET Core Web API & Blazor Client

FullStackHero .NET Starter Kit is a starting point for your next `.NET 8 Clean Architecture` Solution that incorporates the most essential packages and features your projects will ever need including out-of-the-box Multi-Tenancy support. This project can save well over 200+ hours of development time for your team.
FullStackHero .NET Starter Kit is a starting point for your next `.NET 9 Clean Architecture` Solution that incorporates the most essential packages and features your projects will ever need including out-of-the-box Multi-Tenancy support. This project can save well over 200+ hours of development time for your team.

![FullStackHero .NET Starter Kit](./assets/fullstackhero-dotnet-starter-kit.png)

Expand All @@ -16,7 +16,7 @@ As the project is still in beta, the NuGet packages are not yet available. You c

Prerequisites:

- .NET 8 SDK installed.
- .NET 9 SDK installed.
- Visual Studio IDE.
- Docker Desktop.
- PostgreSQL instance running on your machine or docker container.
Expand All @@ -38,8 +38,8 @@ Please follow the below instructions.

# ✨ Technologies

- ASP.NET Core 8
- Entity Framework Core 8
- .NET 9
- Entity Framework Core 9
- Blazor
- MediatR
- PostgreSQL
Expand Down
Binary file modified assets/fullstackhero-dotnet-starter-kit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
72 changes: 37 additions & 35 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,60 +13,61 @@
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="8.0.0" />
<PackageVersion Include="Asp.Versioning.Http" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="Finbuckle.MultiTenant" Version="7.0.2" />
<PackageVersion Include="Finbuckle.MultiTenant.AspNetCore" Version="7.0.2" />
<PackageVersion Include="Finbuckle.MultiTenant.EntityFrameworkCore" Version="7.0.2" />
<PackageVersion Include="Hangfire" Version="1.8.14" />
<PackageVersion Include="Finbuckle.MultiTenant" Version="9.0.0" />
<PackageVersion Include="Finbuckle.MultiTenant.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Finbuckle.MultiTenant.EntityFrameworkCore" Version="9.0.0" />
<PackageVersion Include="Hangfire" Version="1.8.15" />
<PackageVersion Include="Hangfire.MemoryStorage" Version="1.8.1.1" />
<PackageVersion Include="Hangfire.PostgreSql" Version="1.20.9" />
<PackageVersion Include="MailKit" Version="4.7.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.8" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.8" />
<PackageVersion Include="Hangfire.PostgreSql" Version="1.20.10" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.11.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
<PackageVersion Include="Microsoft.NET.Build.Containers" Version="8.0.100" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
<PackageVersion Include="MimeKit" Version="4.7.1" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageVersion Include="MimeKit" Version="4.8.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.0.0-beta.12" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.6" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.0.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.7.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.1.1" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.7.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Mapster" Version="7.4.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.32.0.97167" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="MediatR" Version="12.4.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageVersion Include="Carter" Version="8.2.1" />
<PackageVersion Include="MediatR" Version="12.4.1" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
<PackageVersion Include="Carter" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Serilog" Version="4.0.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageVersion Include="Serilog" Version="4.1.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageVersion Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
<PackageVersion Include="Serilog.Enrichers.Environment" Version="3.0.1" />
<PackageVersion Include="Serilog.Enrichers.Span" Version="3.1.0" />
<PackageVersion Include="Serilog.Exceptions" Version="8.4.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
<PackageVersion Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.Seq" Version="8.0.0" />
Expand All @@ -77,12 +78,13 @@
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
</ItemGroup>
<ItemGroup Label="Aspire">
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.1.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.8.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.1.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" />
<PackageVersion Include="Aspire.AppHost.Sdk" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="9.0.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
Expand Down
2 changes: 1 addition & 1 deletion src/Dockerfile.Blazor
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app

COPY . ./
Expand Down
6 changes: 3 additions & 3 deletions src/Shared/Authorization/AppConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
namespace FSH.Starter.Shared.Authorization;
public static class AppConstants
{
public static readonly Collection<string> SupportedImageFormats = new()
{
public static readonly Collection<string> SupportedImageFormats =
[
".jpeg",
".jpg",
".png"
};
];
public static readonly string StandardImageFormat = "image/jpeg";
public static readonly int MaxImageWidth = 1500;
public static readonly int MaxImageHeight = 1500;
Expand Down
14 changes: 7 additions & 7 deletions src/Shared/Authorization/FshPermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace FSH.Starter.Shared.Authorization;

public static class FshPermissions
{
private static readonly FshPermission[] allPermissions =
{
private static readonly FshPermission[] AllPermissions =
[
//tenants
new("View Tenants", FshActions.View, FshResources.Tenants, IsRoot: true),
new("Create Tenants", FshActions.Create, FshResources.Tenants, IsRoot: true),
Expand Down Expand Up @@ -57,12 +57,12 @@ public static class FshPermissions

//audit
new("View Audit Trails", FshActions.View, FshResources.AuditTrails),
};
];

public static IReadOnlyList<FshPermission> All { get; } = new ReadOnlyCollection<FshPermission>(allPermissions);
public static IReadOnlyList<FshPermission> Root { get; } = new ReadOnlyCollection<FshPermission>(allPermissions.Where(p => p.IsRoot).ToArray());
public static IReadOnlyList<FshPermission> Admin { get; } = new ReadOnlyCollection<FshPermission>(allPermissions.Where(p => !p.IsRoot).ToArray());
public static IReadOnlyList<FshPermission> Basic { get; } = new ReadOnlyCollection<FshPermission>(allPermissions.Where(p => p.IsBasic).ToArray());
public static IReadOnlyList<FshPermission> All { get; } = new ReadOnlyCollection<FshPermission>(AllPermissions);
public static IReadOnlyList<FshPermission> Root { get; } = new ReadOnlyCollection<FshPermission>(AllPermissions.Where(p => p.IsRoot).ToArray());
public static IReadOnlyList<FshPermission> Admin { get; } = new ReadOnlyCollection<FshPermission>(AllPermissions.Where(p => !p.IsRoot).ToArray());
public static IReadOnlyList<FshPermission> Basic { get; } = new ReadOnlyCollection<FshPermission>(AllPermissions.Where(p => p.IsBasic).ToArray());
}

public record FshPermission(string Description, string Action, string Resource, bool IsBasic = false, bool IsRoot = false)
Expand Down
2 changes: 1 addition & 1 deletion src/Shared/Shared.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
18 changes: 9 additions & 9 deletions src/api/framework/Core/Audit/TrailDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ public class TrailDto()
public Guid Id { get; set; }
public DateTimeOffset DateTime { get; set; }
public Guid UserId { get; set; }
public Dictionary<string, object?> KeyValues { get; } = new();
public Dictionary<string, object?> OldValues { get; } = new();
public Dictionary<string, object?> NewValues { get; } = new();
public Collection<string> ModifiedProperties { get; } = new();
public Dictionary<string, object?> KeyValues { get; } = [];
public Dictionary<string, object?> OldValues { get; } = [];
public Dictionary<string, object?> NewValues { get; } = [];
public Collection<string> ModifiedProperties { get; } = [];
public TrailType Type { get; set; }
public string? TableName { get; set; }

private static readonly JsonSerializerOptions serializerOptions = new()
private static readonly JsonSerializerOptions SerializerOptions = new()
{
WriteIndented = false,
};
Expand All @@ -28,10 +28,10 @@ public AuditTrail ToAuditTrail()
Operation = Type.ToString(),
Entity = TableName,
DateTime = DateTime,
PrimaryKey = JsonSerializer.Serialize(KeyValues, serializerOptions),
PreviousValues = OldValues.Count == 0 ? null : JsonSerializer.Serialize(OldValues, serializerOptions),
NewValues = NewValues.Count == 0 ? null : JsonSerializer.Serialize(NewValues, serializerOptions),
ModifiedProperties = ModifiedProperties.Count == 0 ? null : JsonSerializer.Serialize(ModifiedProperties, serializerOptions)
PrimaryKey = JsonSerializer.Serialize(KeyValues, SerializerOptions),
PreviousValues = OldValues.Count == 0 ? null : JsonSerializer.Serialize(OldValues, SerializerOptions),
NewValues = NewValues.Count == 0 ? null : JsonSerializer.Serialize(NewValues, SerializerOptions),
ModifiedProperties = ModifiedProperties.Count == 0 ? null : JsonSerializer.Serialize(ModifiedProperties, SerializerOptions)
};
}
}
2 changes: 1 addition & 1 deletion src/api/framework/Core/Auth/Jwt/JwtOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
{
if (string.IsNullOrEmpty(Key))
{
yield return new ValidationResult("No Key defined in JwtSettings config", new[] { nameof(Key) });
yield return new ValidationResult("No Key defined in JwtSettings config", [nameof(Key)]);
}
}
}
7 changes: 3 additions & 4 deletions src/api/framework/Core/Exceptions/ForbiddenException.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System.Collections.ObjectModel;
using System.Net;
using System.Net;

namespace FSH.Framework.Core.Exceptions;
public class ForbiddenException : FshException
{
public ForbiddenException()
: base("unauthorized", new Collection<string>(), HttpStatusCode.Forbidden)
: base("unauthorized", [], HttpStatusCode.Forbidden)
{
}
public ForbiddenException(string message)
: base(message, new Collection<string>(), HttpStatusCode.Forbidden)
: base(message, [], HttpStatusCode.Forbidden)
{
}
}
12 changes: 6 additions & 6 deletions src/api/framework/Infrastructure/SecurityHeaders/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ internal static IServiceCollection ConfigureSecurityHeaders(this IServiceCollect

return services;
}

internal static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app)
{
var options = app.ApplicationServices.GetRequiredService<IOptions<SecurityHeaderOptions>>().Value;

if (options.Enable)
{
app.Use(async (context, next) =>
Expand Down Expand Up @@ -48,22 +48,22 @@ internal static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder
{
context.Response.Headers.XXSSProtection = options.Headers.XXSSProtection;
}

if (!string.IsNullOrWhiteSpace(options.Headers.ContentSecurityPolicy))
{
context.Response.Headers.ContentSecurityPolicy = options.Headers.ContentSecurityPolicy;
}

if (!string.IsNullOrWhiteSpace(options.Headers.StrictTransportSecurity))
{
context.Response.Headers.StrictTransportSecurity = options.Headers.StrictTransportSecurity;
}
}

await next.Invoke();
});
}

return app;
}
}
6 changes: 3 additions & 3 deletions src/api/framework/Infrastructure/Tenant/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ public static IServiceCollection ConfigureMultitenancy(this IServiceCollection s
{
// to save database calls to resolve tenant
// this was happening for every request earlier, leading to ineffeciency
config.Events.OnTenantResolved = async (context) =>
config.Events.OnTenantResolveCompleted = async (context) =>
{
if (context.StoreType != typeof(DistributedCacheStore<FshTenantInfo>))
if (context.MultiTenantContext.StoreInfo!.StoreType != typeof(DistributedCacheStore<FshTenantInfo>))
{
var sp = ((HttpContext)context.Context!).RequestServices;
var distributedCacheStore = sp
.GetService<IEnumerable<IMultiTenantStore<FshTenantInfo>>>()!
.FirstOrDefault(s => s.GetType() == typeof(DistributedCacheStore<FshTenantInfo>));

await distributedCacheStore!.TryAddAsync((FshTenantInfo)context.TenantInfo!);
await distributedCacheStore!.TryAddAsync(context.MultiTenantContext.TenantInfo!);
}
await Task.FromResult(0);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
name: "RoleNameIndex",
schema: "identity",
table: "Roles",
columns: new[] { "NormalizedName", "TenantId" },
columns: ["NormalizedName", "TenantId"],
unique: true);

migrationBuilder.CreateIndex(
Expand Down
Loading
Loading