Skip to content

Commit

Permalink
feat : upgrade to .NET 9 (#1061)
Browse files Browse the repository at this point in the history
* update nuget and deps to 9.0

* update infra

* update to .net 9

* update docs

* fix issue
  • Loading branch information
iammukeshm authored Nov 22, 2024
1 parent df50461 commit ad36be4
Show file tree
Hide file tree
Showing 33 changed files with 128 additions and 126 deletions.
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

0 comments on commit ad36be4

Please sign in to comment.