diff --git a/MainProject.Tests/Controllers/UserController_Tests.cs b/MainProject.Tests/Controllers/UserController_Tests.cs index 7ece72f..76a1b29 100644 --- a/MainProject.Tests/Controllers/UserController_Tests.cs +++ b/MainProject.Tests/Controllers/UserController_Tests.cs @@ -29,7 +29,7 @@ namespace BasicDotnetTemplate.MainProject.Tests; [TestClass] public class UserControllerTests { - private IMapper _mapper; + private IMapper? _mapper; [TestInitialize] public void Setup() diff --git a/MainProject.Tests/Core/AutoMapperConfiguration_Tests.cs b/MainProject.Tests/Core/AutoMapperConfiguration_Tests.cs index 77b4e48..d52a962 100644 --- a/MainProject.Tests/Core/AutoMapperConfiguration_Tests.cs +++ b/MainProject.Tests/Core/AutoMapperConfiguration_Tests.cs @@ -19,7 +19,7 @@ namespace BasicDotnetTemplate.MainProject.Tests; [TestClass] public class AutoMapperConfiguration_Tests { - private IMapper _mapper; + private IMapper? _mapper; [TestInitialize] public void Setup() @@ -53,13 +53,13 @@ public class AutoMapperConfiguration_Tests }, IsTestUser = true }; - UserDto data = _mapper.Map(user); + UserDto? data = _mapper?.Map(user); - Assert.IsTrue(data.Guid == user.Guid); - Assert.IsTrue(data.Username == user.Username); - Assert.IsTrue(data.FirstName == user.FirstName); - Assert.IsTrue(data.LastName == user.LastName); - Assert.IsTrue(data.Email == user.Email); + Assert.IsTrue(data?.Guid == user.Guid); + Assert.IsTrue(data?.Username == user.Username); + Assert.IsTrue(data?.FirstName == user.FirstName); + Assert.IsTrue(data?.LastName == user.LastName); + Assert.IsTrue(data?.Email == user.Email); } catch (Exception ex) { diff --git a/MainProject.Tests/Models/Api/Response/User/GetUserResponse_Tests.cs b/MainProject.Tests/Models/Api/Response/User/GetUserResponse_Tests.cs index 1ab43ce..c1cb1c1 100644 --- a/MainProject.Tests/Models/Api/Response/User/GetUserResponse_Tests.cs +++ b/MainProject.Tests/Models/Api/Response/User/GetUserResponse_Tests.cs @@ -19,7 +19,7 @@ namespace BasicDotnetTemplate.MainProject.Tests; [TestClass] public class GetUserResponse_Tests { - private IMapper _mapper; + private IMapper? _mapper; [TestInitialize] public void Setup() @@ -97,7 +97,7 @@ public class GetUserResponse_Tests }, IsTestUser = true }; - UserDto data = _mapper.Map(user); + UserDto? data = _mapper?.Map(user); var getUserResponse = new GetUserResponse(200, "This is a test message", data); Assert.IsTrue(getUserResponse.Status == 200 && getUserResponse.Message == "This is a test message" && getUserResponse.Data == data); } diff --git a/MainProject/Core/Database/SqlServerContext.cs b/MainProject/Core/Database/SqlServerContext.cs index e9b15ab..1a7cb22 100644 --- a/MainProject/Core/Database/SqlServerContext.cs +++ b/MainProject/Core/Database/SqlServerContext.cs @@ -13,6 +13,20 @@ namespace BasicDotnetTemplate.MainProject.Core.Database } public DbSet Users { get; set; } + + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasIndex(x => x.Email, "IX_Email"); + + modelBuilder.Entity() + .HasIndex(x => x.Username, "IX_Username"); + + modelBuilder.Entity() + .HasIndex(x => new { x.IsDeleted, x.Guid }, "IX_IsDeleted_Guid") + .HasFilter("[IsDeleted] = 0"); + } } } diff --git a/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.Designer.cs b/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.Designer.cs new file mode 100644 index 0000000..b22b8a0 --- /dev/null +++ b/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.Designer.cs @@ -0,0 +1,170 @@ +// +using System; +using BasicDotnetTemplate.MainProject.Core.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MainProject.Migrations +{ + [DbContext(typeof(SqlServerContext))] + [Migration("20250312234517_AlterTableUserMaxLengthIndexes")] + partial class AlterTableUserMaxLengthIndexes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreationUserId") + .HasColumnType("int"); + + b.Property("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DeletionUserId") + .HasColumnType("int"); + + b.Property("Guid") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("nvarchar(45)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateTime") + .HasColumnType("datetime2"); + + b.Property("UpdateUserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Role"); + }); + + modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreationUserId") + .HasColumnType("int"); + + b.Property("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DeletionUserId") + .HasColumnType("int"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Guid") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("nvarchar(45)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsTestUser") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UpdateTime") + .HasColumnType("datetime2"); + + b.Property("UpdateUserId") + .HasColumnType("int"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex(new[] { "Email" }, "IX_Email"); + + b.HasIndex(new[] { "IsDeleted", "Guid" }, "IX_IsDeleted_Guid") + .HasFilter("[IsDeleted] = 0"); + + b.HasIndex(new[] { "Username" }, "IX_Username"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.User", b => + { + b.HasOne("BasicDotnetTemplate.MainProject.Models.Database.SqlServer.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.cs b/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.cs new file mode 100644 index 0000000..862a803 --- /dev/null +++ b/MainProject/Migrations/20250312234517_AlterTableUserMaxLengthIndexes.cs @@ -0,0 +1,165 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MainProject.Migrations +{ + /// + public partial class AlterTableUserMaxLengthIndexes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Username", + table: "Users", + type: "nvarchar(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "nvarchar(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "Guid", + table: "Users", + type: "nvarchar(45)", + maxLength: 45, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Users", + type: "nvarchar(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Users", + type: "nvarchar(200)", + maxLength: 200, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AddColumn( + name: "IsTestUser", + table: "Users", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AlterColumn( + name: "Guid", + table: "Role", + type: "nvarchar(45)", + maxLength: 45, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.CreateIndex( + name: "IX_Email", + table: "Users", + column: "Email"); + + migrationBuilder.CreateIndex( + name: "IX_IsDeleted_Guid", + table: "Users", + columns: new[] { "IsDeleted", "Guid" }, + filter: "[IsDeleted] = 0"); + + migrationBuilder.CreateIndex( + name: "IX_Username", + table: "Users", + column: "Username"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Email", + table: "Users"); + + migrationBuilder.DropIndex( + name: "IX_IsDeleted_Guid", + table: "Users"); + + migrationBuilder.DropIndex( + name: "IX_Username", + table: "Users"); + + migrationBuilder.DropColumn( + name: "IsTestUser", + table: "Users"); + + migrationBuilder.AlterColumn( + name: "Username", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(200)", + oldMaxLength: 200); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(200)", + oldMaxLength: 200); + + migrationBuilder.AlterColumn( + name: "Guid", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(45)", + oldMaxLength: 45); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(200)", + oldMaxLength: 200); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(200)", + oldMaxLength: 200); + + migrationBuilder.AlterColumn( + name: "Guid", + table: "Role", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(45)", + oldMaxLength: 45); + } + } +} diff --git a/MainProject/Migrations/SqlServerContextModelSnapshot.cs b/MainProject/Migrations/SqlServerContextModelSnapshot.cs index 8368bbd..e0eb021 100644 --- a/MainProject/Migrations/SqlServerContextModelSnapshot.cs +++ b/MainProject/Migrations/SqlServerContextModelSnapshot.cs @@ -44,7 +44,8 @@ namespace MainProject.Migrations b.Property("Guid") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(45) + .HasColumnType("nvarchar(45)"); b.Property("IsDeleted") .HasColumnType("bit"); @@ -86,22 +87,29 @@ namespace MainProject.Migrations b.Property("Email") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("FirstName") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Guid") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(45) + .HasColumnType("nvarchar(45)"); b.Property("IsDeleted") .HasColumnType("bit"); + b.Property("IsTestUser") + .HasColumnType("bit"); + b.Property("LastName") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.Property("Password") .IsRequired() @@ -126,12 +134,20 @@ namespace MainProject.Migrations b.Property("Username") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); b.HasKey("Id"); b.HasIndex("RoleId"); + b.HasIndex(new[] { "Email" }, "IX_Email"); + + b.HasIndex(new[] { "IsDeleted", "Guid" }, "IX_IsDeleted_Guid") + .HasFilter("[IsDeleted] = 0"); + + b.HasIndex(new[] { "Username" }, "IX_Username"); + b.ToTable("Users"); }); diff --git a/MainProject/Models/Database/SqlServer/Base.cs b/MainProject/Models/Database/SqlServer/Base.cs index 6c89599..d90ad6a 100644 --- a/MainProject/Models/Database/SqlServer/Base.cs +++ b/MainProject/Models/Database/SqlServer/Base.cs @@ -1,18 +1,22 @@ -namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; + +namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer; + +public class Base { - public class Base - { - public int Id { get; set; } - public string Guid { get; set; } - public bool IsDeleted { get; set; } - public DateTime CreationTime { get; set; } - public int CreationUserId { get; set; } - public DateTime UpdateTime { get; set; } - public int UpdateUserId { get; set; } - public DateTime DeletionTime { get; set; } - public int DeletionUserId { get; set; } - } + public int Id { get; set; } + [MaxLength(45)] + public string Guid { get; set; } + public bool IsDeleted { get; set; } + public DateTime CreationTime { get; set; } + public int CreationUserId { get; set; } + public DateTime UpdateTime { get; set; } + public int UpdateUserId { get; set; } + public DateTime DeletionTime { get; set; } + public int DeletionUserId { get; set; } } + diff --git a/MainProject/Models/Database/SqlServer/User.cs b/MainProject/Models/Database/SqlServer/User.cs index 7e5f149..4e0f0e8 100644 --- a/MainProject/Models/Database/SqlServer/User.cs +++ b/MainProject/Models/Database/SqlServer/User.cs @@ -1,12 +1,18 @@ +using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer; public class User : Base { + [MaxLength(200)] public required string Username { get; set; } + [MaxLength(200)] public required string FirstName { get; set; } + [MaxLength(200)] public required string LastName { get; set; } + [MaxLength(200)] public required string Email { get; set; } public required string PasswordSalt { get; set; } public required string PasswordHash { get; set; } diff --git a/MainProject/Utils/JwtTokenUtils.cs b/MainProject/Utils/JwtTokenUtils.cs index 7c427a0..3786ec2 100644 --- a/MainProject/Utils/JwtTokenUtils.cs +++ b/MainProject/Utils/JwtTokenUtils.cs @@ -54,8 +54,8 @@ public class JwtTokenUtils string? guid = null; if ( - String.IsNullOrEmpty(_jwtKey) || - String.IsNullOrEmpty(_jwtIssuer) || + String.IsNullOrEmpty(_jwtKey) || + String.IsNullOrEmpty(_jwtIssuer) || String.IsNullOrEmpty(_jwtAudience) ) { @@ -68,7 +68,7 @@ public class JwtTokenUtils token = authorizations[1]; } - if(!String.IsNullOrEmpty(token)) + if (!String.IsNullOrEmpty(token)) { try { @@ -97,9 +97,9 @@ public class JwtTokenUtils } } } - catch(Exception exception) + catch (Exception exception) { - Logger.Error($"[JwtTokenUtils][ValidateToken] | {exception}"); + Logger.Error(exception, $"[JwtTokenUtils][ValidateToken]"); return guid; } }