From ca72a9055d87ea4c77ff3ebcdb990bfdcf40bb96 Mon Sep 17 00:00:00 2001 From: Externius Date: Sun, 27 Jul 2025 11:43:48 +0200 Subject: [PATCH 1/2] Migrate from AutoMapper to Mapster --- Directory.Packages.props | 9 +- .../Services/Models/DungeonOptionModel.cs | 2 +- src/RDMG.Core/ConfigureServices.cs | 6 +- src/RDMG.Core/Domain/DungeonOption.cs | 2 +- src/RDMG.Core/RDMG.Core.csproj | 3 +- src/RDMG.Core/Services/AuthService.cs | 2 +- .../Services/Automapper/DungeonProfile.cs | 14 --- .../Services/Automapper/OptionProfile.cs | 13 --- .../Services/Automapper/UserProfile.cs | 13 --- src/RDMG.Core/Services/DungeonService.cs | 13 +-- src/RDMG.Core/Services/OptionService.cs | 2 +- src/RDMG.Core/Services/UserService.cs | 9 +- src/RDMG.Infrastructure/ConfigureServices.cs | 5 +- .../Data/AppDbContextInitializer.cs | 19 ++-- .../Repository/Automapper/DungeonProfile.cs | 18 ---- .../Repository/Automapper/UserProfile.cs | 13 --- .../Repository/DungeonOptionRepository.cs | 4 +- .../Repository/DungeonRepository.cs | 24 ++--- .../Repository/OptionRepository.cs | 2 +- .../Repository/UserRepository.cs | 2 +- src/RDMG.Web/Automapper/AuthProfile.cs | 13 --- .../Automapper/DungeonOptionProfile.cs | 27 ------ src/RDMG.Web/Automapper/DungeonProfile.cs | 13 --- src/RDMG.Web/Automapper/ProfileProfile.cs | 14 --- src/RDMG.Web/Automapper/UserProfile.cs | 14 --- src/RDMG.Web/ConfigureServices.cs | 45 +++++++++- .../Controllers/Auth/AuthController.cs | 2 +- .../Controllers/Web/DungeonController.cs | 78 +++++++++++----- .../Controllers/Web/HomeController.cs | 4 - .../Controllers/Web/ProfileController.cs | 2 +- .../Controllers/Web/UserController.cs | 2 +- .../Models/Dungeon/DungeonOptionViewModel.cs | 2 +- src/RDMG.Web/Models/User/UserListViewModel.cs | 2 +- src/RDMG.Web/RDMG.Web.csproj | 2 +- src/RDMG.Web/Views/Dungeon/Load.cshtml | 88 ++++++++++--------- src/RDMG.Web/libman.json | 2 +- .../DungeonServiceTests/Create.cs | 3 +- tests/RDMG.Core.Tests/RDMG.Core.Tests.csproj | 1 - 38 files changed, 206 insertions(+), 283 deletions(-) delete mode 100644 src/RDMG.Core/Services/Automapper/DungeonProfile.cs delete mode 100644 src/RDMG.Core/Services/Automapper/OptionProfile.cs delete mode 100644 src/RDMG.Core/Services/Automapper/UserProfile.cs delete mode 100644 src/RDMG.Infrastructure/Repository/Automapper/DungeonProfile.cs delete mode 100644 src/RDMG.Infrastructure/Repository/Automapper/UserProfile.cs delete mode 100644 src/RDMG.Web/Automapper/AuthProfile.cs delete mode 100644 src/RDMG.Web/Automapper/DungeonOptionProfile.cs delete mode 100644 src/RDMG.Web/Automapper/DungeonProfile.cs delete mode 100644 src/RDMG.Web/Automapper/ProfileProfile.cs delete mode 100644 src/RDMG.Web/Automapper/UserProfile.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index ebcca42..4cdcdca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,9 +3,10 @@ true - + + @@ -20,11 +21,11 @@ - + - - + + \ No newline at end of file diff --git a/src/RDMG.Core/Abstractions/Services/Models/DungeonOptionModel.cs b/src/RDMG.Core/Abstractions/Services/Models/DungeonOptionModel.cs index 35403d1..4f568d0 100644 --- a/src/RDMG.Core/Abstractions/Services/Models/DungeonOptionModel.cs +++ b/src/RDMG.Core/Abstractions/Services/Models/DungeonOptionModel.cs @@ -19,5 +19,5 @@ public class DungeonOptionModel : EditModel public int RoamingPercent { get; set; } public int Width { get; set; } = 800; public int Height { get; set; } = 800; - public IEnumerable Dungeons { get; } = new List(); + public List Dungeons { get; set; } = []; } \ No newline at end of file diff --git a/src/RDMG.Core/ConfigureServices.cs b/src/RDMG.Core/ConfigureServices.cs index 22bb9ed..a064280 100644 --- a/src/RDMG.Core/ConfigureServices.cs +++ b/src/RDMG.Core/ConfigureServices.cs @@ -1,9 +1,9 @@ -using Microsoft.Extensions.DependencyInjection; +using Mapster; +using Microsoft.Extensions.DependencyInjection; using RDMG.Core.Abstractions.Generator; using RDMG.Core.Abstractions.Services; using RDMG.Core.Generator; using RDMG.Core.Services; -using RDMG.Core.Services.Automapper; namespace RDMG.Core; @@ -22,7 +22,7 @@ this IServiceCollection services .AddScoped() .AddScoped(); - services.AddAutoMapper(cfg => { cfg.AllowNullCollections = true; }, typeof(DungeonProfile)); + services.AddMapster(); return services; } diff --git a/src/RDMG.Core/Domain/DungeonOption.cs b/src/RDMG.Core/Domain/DungeonOption.cs index 2918533..5dcb219 100644 --- a/src/RDMG.Core/Domain/DungeonOption.cs +++ b/src/RDMG.Core/Domain/DungeonOption.cs @@ -19,5 +19,5 @@ public class DungeonOption : AuditableEntity public int UserId { get; set; } public User? User { get; set; } - public IEnumerable Dungeons { get; } = new List(); + public List Dungeons { get; } = []; } \ No newline at end of file diff --git a/src/RDMG.Core/RDMG.Core.csproj b/src/RDMG.Core/RDMG.Core.csproj index 3b8c2ab..e6a2fdd 100644 --- a/src/RDMG.Core/RDMG.Core.csproj +++ b/src/RDMG.Core/RDMG.Core.csproj @@ -1,6 +1,7 @@  - + + diff --git a/src/RDMG.Core/Services/AuthService.cs b/src/RDMG.Core/Services/AuthService.cs index 5522ba4..0ea2d59 100644 --- a/src/RDMG.Core/Services/AuthService.cs +++ b/src/RDMG.Core/Services/AuthService.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Repository; using RDMG.Core.Abstractions.Services; diff --git a/src/RDMG.Core/Services/Automapper/DungeonProfile.cs b/src/RDMG.Core/Services/Automapper/DungeonProfile.cs deleted file mode 100644 index e8acb26..0000000 --- a/src/RDMG.Core/Services/Automapper/DungeonProfile.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Core.Domain; - -namespace RDMG.Core.Services.Automapper; - -public class DungeonProfile : Profile -{ - public DungeonProfile() - { - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Core/Services/Automapper/OptionProfile.cs b/src/RDMG.Core/Services/Automapper/OptionProfile.cs deleted file mode 100644 index 566b643..0000000 --- a/src/RDMG.Core/Services/Automapper/OptionProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Core.Domain; - -namespace RDMG.Core.Services.Automapper; - -public class OptionProfile : Profile -{ - public OptionProfile() - { - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Core/Services/Automapper/UserProfile.cs b/src/RDMG.Core/Services/Automapper/UserProfile.cs deleted file mode 100644 index 0f6bf02..0000000 --- a/src/RDMG.Core/Services/Automapper/UserProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Core.Domain; - -namespace RDMG.Core.Services.Automapper; - -public class UserProfile : Profile -{ - public UserProfile() - { - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Core/Services/DungeonService.cs b/src/RDMG.Core/Services/DungeonService.cs index 668dc43..8d507d1 100644 --- a/src/RDMG.Core/Services/DungeonService.cs +++ b/src/RDMG.Core/Services/DungeonService.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Generator; using RDMG.Core.Abstractions.Repository; @@ -208,8 +208,9 @@ public async Task> GetAllDungeonOptionsForUserAs { try { - return _mapper.Map( - await _dungeonOptionRepository.GetDungeonOptionByNameAsync(dungeonName, userId, cancellationToken)); + var model = await _dungeonOptionRepository.GetDungeonOptionByNameAsync(dungeonName, userId, + cancellationToken); + return model is not null ? _mapper.Map(model) : null; } catch (Exception ex) { @@ -278,7 +279,8 @@ public async Task GetDungeonAsync(int id, CancellationToken cancel { try { - return _mapper.Map(await _dungeonRepository.GetDungeonAsync(id, cancellationToken)); + return _mapper.Map(await _dungeonRepository.GetDungeonAsync(id, cancellationToken) ?? + throw new ServiceException(Error.NotFound)); } catch (Exception ex) { @@ -310,7 +312,8 @@ public async Task GetDungeonOptionAsync(int id, Cancellation try { return _mapper.Map( - await _dungeonOptionRepository.GetDungeonOptionAsync(id, cancellationToken)); + await _dungeonOptionRepository.GetDungeonOptionAsync(id, cancellationToken) ?? + throw new ServiceException(Error.NotFound)); } catch (Exception ex) { diff --git a/src/RDMG.Core/Services/OptionService.cs b/src/RDMG.Core/Services/OptionService.cs index b375aed..7483c21 100644 --- a/src/RDMG.Core/Services/OptionService.cs +++ b/src/RDMG.Core/Services/OptionService.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Repository; diff --git a/src/RDMG.Core/Services/UserService.cs b/src/RDMG.Core/Services/UserService.cs index fbe3d13..0b59268 100644 --- a/src/RDMG.Core/Services/UserService.cs +++ b/src/RDMG.Core/Services/UserService.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Repository; using RDMG.Core.Abstractions.Services; @@ -18,7 +18,7 @@ public class UserService(IMapper mapper, IUserRepository userRepository, ILogger public async Task CreateAsync(UserModel model) { ValidateModel(model); - await CheckUserExist(model); + await CheckUserExistAsync(model); try { model.Password = PasswordHelper.EncryptPassword(model.Password); @@ -53,7 +53,7 @@ private static void ValidateModel(UserModel model) throw new ServiceAggregateException(errors); } - private async Task CheckUserExist(UserModel model) + private async Task CheckUserExistAsync(UserModel model) { var user = await _userRepository.GetByUsernameAsync(model.Username, null); if (user is not null) @@ -77,7 +77,8 @@ public async Task GetAsync(int id) { try { - var user = await _userRepository.GetAsync(id); + var user = await _userRepository.GetAsync(id) ?? + throw new ServiceException(Resources.Error.NotFound); return _mapper.Map(user); } diff --git a/src/RDMG.Infrastructure/ConfigureServices.cs b/src/RDMG.Infrastructure/ConfigureServices.cs index 4fcf074..62797c7 100644 --- a/src/RDMG.Infrastructure/ConfigureServices.cs +++ b/src/RDMG.Infrastructure/ConfigureServices.cs @@ -10,7 +10,7 @@ using RDMG.Infrastructure.Interceptors; using RDMG.Infrastructure.Repository; using System.Reflection; -using RDMG.Infrastructure.Repository.Automapper; +using Mapster; namespace RDMG.Infrastructure; @@ -77,7 +77,8 @@ private static void Configure(IServiceCollection services, IConfiguration config .AddScoped() .AddScoped() .AddScoped(); - services.AddAutoMapper(cfg => { cfg.AllowNullCollections = true; }, typeof(DungeonProfile)); + + services.AddMapster(); } public static IServiceCollection AddTestInfrastructureServices(this IServiceCollection services, diff --git a/src/RDMG.Infrastructure/Data/AppDbContextInitializer.cs b/src/RDMG.Infrastructure/Data/AppDbContextInitializer.cs index cbd3c13..8dd5276 100644 --- a/src/RDMG.Infrastructure/Data/AppDbContextInitializer.cs +++ b/src/RDMG.Infrastructure/Data/AppDbContextInitializer.cs @@ -8,10 +8,13 @@ using RDMG.Core.Helpers; namespace RDMG.Infrastructure.Data; + public class AppDbContextInitializer(IAppDbContext context, IDungeonService dungeonService, IOptions config) { private readonly IAppDbContext _context = context; private readonly IDungeonService _dungeonService = dungeonService; + private const string UtDungeonName1 = "Test 1"; + public const string UtDungeonName2 = "Test 2"; public async Task UpdateAsync(CancellationToken cancellationToken) { @@ -24,13 +27,14 @@ public async Task UpdateAsync(CancellationToken cancellationToken) await _context.Database.EnsureCreatedAsync(cancellationToken); } } + public async Task SeedDataAsync(CancellationToken cancellationToken) { if (!_context.Users.Any()) { await SeedUsersAsync(cancellationToken); await SeedOptionsAsync(cancellationToken); - await SeedDungeonsAsync(cancellationToken, 1); + await SeedDungeonsAsync(1, cancellationToken); } } @@ -40,8 +44,8 @@ public async Task SeedTestBaseAsync(CancellationToken cancellationToken) { await SeedUsersAsync(cancellationToken); await SeedOptionsAsync(cancellationToken); - await SeedDungeonsAsync(cancellationToken, 1); - await SeedDungeonsAsync(cancellationToken, 2); + await SeedDungeonsAsync(1, cancellationToken); + await SeedDungeonsAsync(2, cancellationToken); } } @@ -345,7 +349,7 @@ private async Task SeedTreasureValueAsync(CancellationToken token) new() { Key = OptionKey.TreasureValue, - Name = Resources.Common.Standard, + Name = Resources.Common.Standard, Value = "1" }, new() @@ -419,13 +423,12 @@ private async Task SeedSizeAsync(CancellationToken token) await _context.SaveChangesAsync(token); } - private async Task SeedDungeonsAsync(CancellationToken token, - int userId) + private async Task SeedDungeonsAsync(int userId, CancellationToken token) { var dungeonOption = new DungeonOption { UserId = userId, - DungeonName = "Test 1", + DungeonName = UtDungeonName1, Created = DateTime.UtcNow, ItemsRarity = 1, DeadEnd = true, @@ -473,7 +476,7 @@ private async Task SeedDungeonsAsync(CancellationToken token, dungeonOption = new DungeonOption { UserId = userId, - DungeonName = "Test 2", + DungeonName = UtDungeonName2, Created = DateTime.UtcNow, ItemsRarity = 1, DeadEnd = true, diff --git a/src/RDMG.Infrastructure/Repository/Automapper/DungeonProfile.cs b/src/RDMG.Infrastructure/Repository/Automapper/DungeonProfile.cs deleted file mode 100644 index 2b0d3b5..0000000 --- a/src/RDMG.Infrastructure/Repository/Automapper/DungeonProfile.cs +++ /dev/null @@ -1,18 +0,0 @@ -using AutoMapper; -using RDMG.Core.Domain; - -namespace RDMG.Infrastructure.Repository.Automapper; - -public class DungeonProfile : Profile -{ - public DungeonProfile() - { - CreateMap() - .ForMember(d => d.Id, o => o.Ignore()) - .ForMember(d => d.Dungeons, o => o.Ignore()) - .ForMember(d => d.User, opt => opt.Ignore()); - CreateMap() - .ForMember(d => d.Id, o => o.Ignore()) - .ForMember(d => d.DungeonOption, opt => opt.Ignore()); - } -} \ No newline at end of file diff --git a/src/RDMG.Infrastructure/Repository/Automapper/UserProfile.cs b/src/RDMG.Infrastructure/Repository/Automapper/UserProfile.cs deleted file mode 100644 index 9f7089a..0000000 --- a/src/RDMG.Infrastructure/Repository/Automapper/UserProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; -using RDMG.Core.Domain; - -namespace RDMG.Infrastructure.Repository.Automapper; - -public class UserProfile : Profile -{ - public UserProfile() - { - CreateMap() - .ForMember(x => x.Id, o => o.Ignore()); - } -} \ No newline at end of file diff --git a/src/RDMG.Infrastructure/Repository/DungeonOptionRepository.cs b/src/RDMG.Infrastructure/Repository/DungeonOptionRepository.cs index 903962d..fa79915 100644 --- a/src/RDMG.Infrastructure/Repository/DungeonOptionRepository.cs +++ b/src/RDMG.Infrastructure/Repository/DungeonOptionRepository.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Data; @@ -43,7 +43,7 @@ public async Task AddDungeonOptionAsync(DungeonOption dungeonOpti public async Task GetDungeonOptionByNameAsync(string dungeonName, int userId, CancellationToken cancellationToken) { - return await _context.DungeonOptions + return await _context.DungeonOptions .Include(d => d.Dungeons) .AsNoTracking() .Where(d => d.DungeonName == dungeonName && d.UserId == userId) diff --git a/src/RDMG.Infrastructure/Repository/DungeonRepository.cs b/src/RDMG.Infrastructure/Repository/DungeonRepository.cs index 80baa0b..447ddf0 100644 --- a/src/RDMG.Infrastructure/Repository/DungeonRepository.cs +++ b/src/RDMG.Infrastructure/Repository/DungeonRepository.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Data; @@ -7,7 +7,8 @@ namespace RDMG.Infrastructure.Repository; -public class DungeonRepository(IAppDbContext context, IMapper mapper, ILogger logger) : IDungeonRepository +public class DungeonRepository(IAppDbContext context, IMapper mapper, ILogger logger) + : IDungeonRepository { private readonly IAppDbContext _context = context; private readonly ILogger _logger = logger; @@ -16,12 +17,12 @@ public class DungeonRepository(IAppDbContext context, IMapper mapper, ILogger> GetAllDungeonsForUserAsync(int userId, CancellationToken cancellationToken) { return await _context.DungeonOptions - .AsNoTracking() - .Include(d => d.Dungeons) - .Where(d => d.UserId == userId) - .OrderBy(d => d.Created) - .SelectMany(d => d.Dungeons) - .ToListAsync(cancellationToken); + .AsNoTracking() + .Include(d => d.Dungeons) + .Where(d => d.UserId == userId) + .OrderBy(d => d.Created) + .SelectMany(d => d.Dungeons) + .ToListAsync(cancellationToken); } public async Task AddDungeonAsync(Dungeon savedDungeon, CancellationToken cancellationToken) @@ -47,7 +48,8 @@ public async Task DeleteDungeonAsync(int id, CancellationToken cancellatio return false; } - public async Task> GetAllDungeonByOptionNameForUserAsync(string dungeonName, int userId, CancellationToken cancellationToken) + public async Task> GetAllDungeonByOptionNameForUserAsync(string dungeonName, int userId, + CancellationToken cancellationToken) { return await _context.DungeonOptions .AsNoTracking() @@ -60,9 +62,7 @@ public async Task> GetAllDungeonByOptionNameForUserAsync(st public async Task GetDungeonAsync(int id, CancellationToken cancellationToken) { - return await _context.Dungeons - .AsNoTracking() - .FirstOrDefaultAsync(d => d.Id == id, cancellationToken); + return await _context.Dungeons.FirstOrDefaultAsync(d => d.Id == id, cancellationToken); } public async Task UpdateDungeonAsync(Dungeon dungeon, CancellationToken cancellationToken) diff --git a/src/RDMG.Infrastructure/Repository/OptionRepository.cs b/src/RDMG.Infrastructure/Repository/OptionRepository.cs index e7b21e4..d9a4027 100644 --- a/src/RDMG.Infrastructure/Repository/OptionRepository.cs +++ b/src/RDMG.Infrastructure/Repository/OptionRepository.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Data; diff --git a/src/RDMG.Infrastructure/Repository/UserRepository.cs b/src/RDMG.Infrastructure/Repository/UserRepository.cs index 32e054f..2e5ad35 100644 --- a/src/RDMG.Infrastructure/Repository/UserRepository.cs +++ b/src/RDMG.Infrastructure/Repository/UserRepository.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using RDMG.Core.Abstractions.Data; diff --git a/src/RDMG.Web/Automapper/AuthProfile.cs b/src/RDMG.Web/Automapper/AuthProfile.cs deleted file mode 100644 index 9f35d05..0000000 --- a/src/RDMG.Web/Automapper/AuthProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Web.Models.Auth; - -namespace RDMG.Web.Automapper; - -public class AuthProfile : Profile -{ - public AuthProfile() - { - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Web/Automapper/DungeonOptionProfile.cs b/src/RDMG.Web/Automapper/DungeonOptionProfile.cs deleted file mode 100644 index b1239b6..0000000 --- a/src/RDMG.Web/Automapper/DungeonOptionProfile.cs +++ /dev/null @@ -1,27 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Web.Models.Dungeon; -using System.Globalization; - -namespace RDMG.Web.Automapper; - -public class DungeonOptionProfile : Profile -{ - public DungeonOptionProfile() - { - CreateMap() - .ForMember(dest => dest.TreasureValue, - opt => opt.MapFrom(src => Convert.ToDouble(src.TreasureValue, CultureInfo.InvariantCulture))) - .ForMember(dest => dest.MonsterType, opt => opt.Ignore()); - CreateMap() - .ForMember(dest => dest.TreasureValue, - opt => opt.MapFrom(src => src.TreasureValue.ToString(CultureInfo.InvariantCulture))) - .ForMember(dest => dest.MonsterType, opt => opt.MapFrom(src => GetMonsters(src))); - CreateMap().ReverseMap(); - } - - private static string[] GetMonsters(DungeonOptionModel model) - { - return model.MonsterType.Split(','); - } -} \ No newline at end of file diff --git a/src/RDMG.Web/Automapper/DungeonProfile.cs b/src/RDMG.Web/Automapper/DungeonProfile.cs deleted file mode 100644 index fb4a587..0000000 --- a/src/RDMG.Web/Automapper/DungeonProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Web.Models.Dungeon; - -namespace RDMG.Web.Automapper; - -public class DungeonProfile : Profile -{ - public DungeonProfile() - { - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Web/Automapper/ProfileProfile.cs b/src/RDMG.Web/Automapper/ProfileProfile.cs deleted file mode 100644 index 9719288..0000000 --- a/src/RDMG.Web/Automapper/ProfileProfile.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Web.Models.Profile; - -namespace RDMG.Web.Automapper; - -public class ProfileProfile : Profile -{ - public ProfileProfile() - { - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Web/Automapper/UserProfile.cs b/src/RDMG.Web/Automapper/UserProfile.cs deleted file mode 100644 index 1d8b4e0..0000000 --- a/src/RDMG.Web/Automapper/UserProfile.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoMapper; -using RDMG.Core.Abstractions.Services.Models; -using RDMG.Web.Models.User; - -namespace RDMG.Web.Automapper; - -public class UserProfile : Profile -{ - public UserProfile() - { - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); - } -} \ No newline at end of file diff --git a/src/RDMG.Web/ConfigureServices.cs b/src/RDMG.Web/ConfigureServices.cs index b5455b7..80be355 100644 --- a/src/RDMG.Web/ConfigureServices.cs +++ b/src/RDMG.Web/ConfigureServices.cs @@ -1,8 +1,11 @@ -using Microsoft.AspNetCore.Authentication.Cookies; +using System.Globalization; +using Mapster; +using Microsoft.AspNetCore.Authentication.Cookies; using RDMG.Core.Abstractions.Services; using RDMG.Core.Abstractions.Services.Exceptions; +using RDMG.Core.Abstractions.Services.Models; using RDMG.Infrastructure.Data; -using RDMG.Web.Automapper; +using RDMG.Web.Models.Dungeon; using RDMG.Web.Services; using Serilog; @@ -26,8 +29,8 @@ public static IServiceCollection AddWebServices(this IServiceCollection services options.LoginPath = new PathString("/Auth/Login"); options.AccessDeniedPath = new PathString("/Auth/Forbidden/"); }); - services.AddAutoMapper(cfg => { cfg.AllowNullCollections = true; }, typeof(AuthProfile)); - services.AddMemoryCache(); + services.ConfigureMapster() + .AddMemoryCache(); services.AddMvc() #if DEBUG @@ -40,6 +43,40 @@ public static IServiceCollection AddWebServices(this IServiceCollection services return services; } + private static IServiceCollection ConfigureMapster(this IServiceCollection services) + { + services.AddMapster(); + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.DeadEnds) + .Ignore(dest => dest.Corridors) + .Ignore(dest => dest.DungeonSizes) + .Ignore(dest => dest.DungeonDifficulties) + .Ignore(dest => dest.PartyLevels) + .Ignore(dest => dest.PartySizes) + .Ignore(dest => dest.TreasureValues) + .Ignore(dest => dest.ItemsRarities) + .Ignore(dest => dest.RoomDensities) + .Ignore(dest => dest.RoomSizes) + .Ignore(dest => dest.MonsterTypes) + .Ignore(dest => dest.TrapPercents) + .Ignore(dest => dest.Themes) + .Ignore(dest => dest.RoamingPercents) + .Map(dest => dest.MonsterType, src => GetMonsters(src)) + .Map(dest => dest.TreasureValue, src => src.TreasureValue.ToString(CultureInfo.InvariantCulture)); + TypeAdapterConfig + .NewConfig() + .Ignore(dest => dest.MonsterType) + .Map(dest => dest.TreasureValue, src => Convert.ToDouble(src.TreasureValue, CultureInfo.InvariantCulture)); + + return services; + } + + private static string[] GetMonsters(DungeonOptionModel model) + { + return model.MonsterType.Split(','); + } + public static IHostBuilder AddSerilog(this IHostBuilder host, IConfiguration configuration) { diff --git a/src/RDMG.Web/Controllers/Auth/AuthController.cs b/src/RDMG.Web/Controllers/Auth/AuthController.cs index c721883..ca7e163 100644 --- a/src/RDMG.Web/Controllers/Auth/AuthController.cs +++ b/src/RDMG.Web/Controllers/Auth/AuthController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using IdentityModel; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; diff --git a/src/RDMG.Web/Controllers/Web/DungeonController.cs b/src/RDMG.Web/Controllers/Web/DungeonController.cs index d47a8bf..dc262f2 100644 --- a/src/RDMG.Web/Controllers/Web/DungeonController.cs +++ b/src/RDMG.Web/Controllers/Web/DungeonController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; @@ -7,11 +7,14 @@ using RDMG.Core.Domain; using RDMG.Web.Models.Dungeon; using System.Text.Json; +using RDMG.Core.Abstractions.Services.Exceptions; +using RDMG.Resources; namespace RDMG.Web.Controllers.Web; [Authorize] -public class DungeonController(IDungeonService dungeonService, +public class DungeonController( + IDungeonService dungeonService, IOptionService optionService, ICurrentUserService currentUserService, IMapper mapper, @@ -26,7 +29,8 @@ public class DungeonController(IDungeonService dungeonService, public async Task Index(CancellationToken cancellationToken) { var list = - await _dungeonService.GetAllDungeonOptionsForUserAsync(_currentUserService.GetUserIdAsInt(), cancellationToken); + await _dungeonService.GetAllDungeonOptionsForUserAsync(_currentUserService.GetUserIdAsInt(), + cancellationToken); var model = new DungeonListViewModel { List = list.Select(_mapper.Map) @@ -40,13 +44,15 @@ public async Task Load(string name, int level, CancellationToken var model = new LoadViewModel { Theme = string.Empty, - Option = _mapper.Map(await _dungeonService.GetDungeonOptionByNameAsync(name, _currentUserService.GetUserIdAsInt(), cancellationToken)), + Option = _mapper.Map( + await _dungeonService.GetDungeonOptionByNameAsync(name, _currentUserService.GetUserIdAsInt(), + cancellationToken) ?? throw new ServiceException(Error.NotFound)), Themes = (await _optionService.ListOptionsAsync(OptionKey.Theme, cancellationToken)) - .Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }) }; if (level != 0) - model.Option.Dungeons = model.Option.Dungeons.Where(dm => dm.Level == level); + model.Option.Dungeons = model.Option.Dungeons.Where(dm => dm.Level == level).ToList(); ViewData["ReturnUrl"] = Url.Action("Index", "Dungeon"); return View(model); @@ -64,6 +70,7 @@ public async Task Delete(int id, CancellationToken cancellationTo { _logger.LogError(ex, "Error deleting dungeon."); } + return RedirectToAction("Index"); } @@ -85,18 +92,24 @@ public async Task Rename(DungeonRenameViewModel model, Cancellati { try { - var existing = await _dungeonService.GetDungeonOptionByNameAsync(model.NewDungeonName, model.UserId, cancellationToken); + var existing = + await _dungeonService.GetDungeonOptionByNameAsync(model.NewDungeonName, model.UserId, + cancellationToken); if (existing is null) { - await _dungeonService.RenameDungeonAsync(model.Id, model.UserId, model.NewDungeonName, cancellationToken); + await _dungeonService.RenameDungeonAsync(model.Id, model.UserId, model.NewDungeonName, + cancellationToken); return RedirectToAction("Index"); } - ModelState.AddModelError(nameof(model.NewDungeonName), string.Format(Resources.Error.DungeonExist, model.NewDungeonName)); + + ModelState.AddModelError(nameof(model.NewDungeonName), + string.Format(Error.DungeonExist, model.NewDungeonName)); } catch (Exception ex) { _logger.LogError(ex, "Error renaming dungeon."); } + return View(model); } @@ -112,10 +125,12 @@ public async Task DeleteOption(int id, CancellationToken cancella { _logger.LogError(ex, "Error deleting dungeon option."); } + return RedirectToAction("Index"); } - private async Task GetMonsterTypeAsync(DungeonOptionCreateViewModel model, CancellationToken cancellationToken) + private async Task GetMonsterTypeAsync(DungeonOptionCreateViewModel model, + CancellationToken cancellationToken) { var monsterType = string.Empty; var monsters = await _optionService.ListOptionsAsync(OptionKey.MonsterType, cancellationToken); @@ -131,6 +146,7 @@ private async Task GetMonsterTypeAsync(DungeonOptionCreateViewModel mode { monsterType = string.Join(",", model.MonsterType); } + return monsterType; } @@ -144,7 +160,8 @@ public async Task Create(DungeonOptionCreateViewModel model, Canc { var optionModel = _mapper.Map(model); optionModel.MonsterType = await GetMonsterTypeAsync(model, cancellationToken); - var dungeon = await _dungeonService.CreateOrUpdateDungeonAsync(optionModel, model.AddDungeon, model.Level, cancellationToken); + var dungeon = await _dungeonService.CreateOrUpdateDungeonAsync(optionModel, model.AddDungeon, + model.Level, cancellationToken); return Json(JsonSerializer.Serialize(dungeon)); } catch (Exception ex) @@ -152,6 +169,7 @@ public async Task Create(DungeonOptionCreateViewModel model, Canc _logger.LogError(ex, "Error creating dungeon."); } } + await FillCreateModelDropDownsAsync(model, cancellationToken); return View(model); } @@ -183,32 +201,43 @@ public async Task Create(int optionId, CancellationToken cancella return View(model); } - private async Task FillCreateModelDropDownsAsync(DungeonOptionCreateViewModel model, CancellationToken cancellationToken) + private async Task FillCreateModelDropDownsAsync(DungeonOptionCreateViewModel model, + CancellationToken cancellationToken) { var options = await _optionService.ListOptionsAsync(null, cancellationToken); var optionModels = options.ToList(); - model.DungeonSizes = optionModels.Where(om => om.Key == OptionKey.Size).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); - model.DungeonDifficulties = optionModels.Where(om => om.Key == OptionKey.Difficulty).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.DungeonSizes = optionModels.Where(om => om.Key == OptionKey.Size) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.DungeonDifficulties = optionModels.Where(om => om.Key == OptionKey.Difficulty) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); model.PartyLevels = GenerateIntSelectList(1, 21); model.PartySizes = GenerateIntSelectList(1, 9); - model.TreasureValues = optionModels.Where(om => om.Key == OptionKey.TreasureValue).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); - model.ItemsRarities = optionModels.Where(om => om.Key == OptionKey.ItemsRarity).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); - model.RoomDensities = optionModels.Where(om => om.Key == OptionKey.RoomDensity).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); - model.RoomSizes = optionModels.Where(om => om.Key == OptionKey.RoomSize).Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); - model.MonsterTypes = optionModels.Where(om => om.Key == OptionKey.MonsterType).Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); - model.TrapPercents = optionModels.Where(om => om.Key == OptionKey.TrapPercent).Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); + model.TreasureValues = optionModels.Where(om => om.Key == OptionKey.TreasureValue) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.ItemsRarities = optionModels.Where(om => om.Key == OptionKey.ItemsRarity) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.RoomDensities = optionModels.Where(om => om.Key == OptionKey.RoomDensity) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.RoomSizes = optionModels.Where(om => om.Key == OptionKey.RoomSize) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value }); + model.MonsterTypes = optionModels.Where(om => om.Key == OptionKey.MonsterType).Select(om => + new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); + model.TrapPercents = optionModels.Where(om => om.Key == OptionKey.TrapPercent).Select(om => + new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); model.DeadEnds = GetBool(); model.Corridors = GetBool(); - model.RoamingPercents = optionModels.Where(om => om.Key == OptionKey.RoamingPercent).Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); - model.Themes = optionModels.Where(om => om.Key == OptionKey.Theme).Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); + model.RoamingPercents = optionModels.Where(om => om.Key == OptionKey.RoamingPercent) + .Select(om => new SelectListItem { Text = om.Name, Value = om.Value, Selected = true }); + model.Themes = optionModels.Where(om => om.Key == OptionKey.Theme).Select(om => new SelectListItem + { Text = om.Name, Value = om.Value, Selected = true }); } private static List GetBool() { return [ - new() { Text = Resources.Common.Yes, Value = "true", Selected = true }, - new() { Text = Resources.Common.No, Value = "false" } + new SelectListItem { Text = Common.Yes, Value = "true", Selected = true }, + new SelectListItem { Text = Common.No, Value = "false" } ]; } @@ -219,6 +248,7 @@ private static List GenerateIntSelectList(int from, int to) { list.Add(new SelectListItem { Text = i.ToString(), Value = i.ToString() }); } + return list; } } \ No newline at end of file diff --git a/src/RDMG.Web/Controllers/Web/HomeController.cs b/src/RDMG.Web/Controllers/Web/HomeController.cs index d7dc90a..2c68cbd 100644 --- a/src/RDMG.Web/Controllers/Web/HomeController.cs +++ b/src/RDMG.Web/Controllers/Web/HomeController.cs @@ -12,11 +12,7 @@ public IActionResult Index() { return RedirectToAction("Login", "Auth"); } - return View(); - } - public IActionResult Privacy() - { return View(); } diff --git a/src/RDMG.Web/Controllers/Web/ProfileController.cs b/src/RDMG.Web/Controllers/Web/ProfileController.cs index cadd1cf..d8b1815 100644 --- a/src/RDMG.Web/Controllers/Web/ProfileController.cs +++ b/src/RDMG.Web/Controllers/Web/ProfileController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using RDMG.Core.Abstractions.Services; diff --git a/src/RDMG.Web/Controllers/Web/UserController.cs b/src/RDMG.Web/Controllers/Web/UserController.cs index f9da8f8..54f0310 100644 --- a/src/RDMG.Web/Controllers/Web/UserController.cs +++ b/src/RDMG.Web/Controllers/Web/UserController.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using MapsterMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using RDMG.Core.Abstractions.Services; diff --git a/src/RDMG.Web/Models/Dungeon/DungeonOptionViewModel.cs b/src/RDMG.Web/Models/Dungeon/DungeonOptionViewModel.cs index 2f3635d..46060b2 100644 --- a/src/RDMG.Web/Models/Dungeon/DungeonOptionViewModel.cs +++ b/src/RDMG.Web/Models/Dungeon/DungeonOptionViewModel.cs @@ -33,5 +33,5 @@ public class DungeonOptionViewModel : EditViewModel public bool Corridor { get; set; } [Required] public int RoamingPercent { get; set; } - public IEnumerable Dungeons { get; set; } = new List(); + public List Dungeons { get; set; } = []; } \ No newline at end of file diff --git a/src/RDMG.Web/Models/User/UserListViewModel.cs b/src/RDMG.Web/Models/User/UserListViewModel.cs index 64b7b57..dd743b9 100644 --- a/src/RDMG.Web/Models/User/UserListViewModel.cs +++ b/src/RDMG.Web/Models/User/UserListViewModel.cs @@ -2,5 +2,5 @@ public class UserListViewModel { - public IEnumerable Details { get; set; } = new List(); + public IEnumerable Details { get; set; } = []; } \ No newline at end of file diff --git a/src/RDMG.Web/RDMG.Web.csproj b/src/RDMG.Web/RDMG.Web.csproj index a5a3f59..a86fbbb 100644 --- a/src/RDMG.Web/RDMG.Web.csproj +++ b/src/RDMG.Web/RDMG.Web.csproj @@ -2,7 +2,7 @@ true true - 1.0.3.1 + 1.0.4.0 b2a492d1-90ea-4e42-b3e5-5d962495bef1 diff --git a/src/RDMG.Web/Views/Dungeon/Load.cshtml b/src/RDMG.Web/Views/Dungeon/Load.cshtml index 779885b..a7cdc83 100644 --- a/src/RDMG.Web/Views/Dungeon/Load.cshtml +++ b/src/RDMG.Web/Views/Dungeon/Load.cshtml @@ -5,7 +5,8 @@ }
- +
@@ -14,7 +15,7 @@ - +
@@ -27,59 +28,60 @@
- @foreach (var dungeon in Model.Option.Dungeons) + @foreach (var dungeon in Model.Option.Dungeons.OrderBy(d => d.Level)) {

@Resources.Dungeon.Level - @dungeon.Level

- +
- +
}
@section Scripts { - + } \ No newline at end of file diff --git a/src/RDMG.Web/libman.json b/src/RDMG.Web/libman.json index 9f65deb..6d9007d 100644 --- a/src/RDMG.Web/libman.json +++ b/src/RDMG.Web/libman.json @@ -11,7 +11,7 @@ "destination": "wwwroot/lib/bootstrap" }, { - "library": "bootbox.js@6.0.2", + "library": "bootbox.js@6.0.4", "destination": "wwwroot/lib/bootbox" }, { diff --git a/tests/RDMG.Core.Tests/DungeonServiceTests/Create.cs b/tests/RDMG.Core.Tests/DungeonServiceTests/Create.cs index e18fcf5..9368723 100644 --- a/tests/RDMG.Core.Tests/DungeonServiceTests/Create.cs +++ b/tests/RDMG.Core.Tests/DungeonServiceTests/Create.cs @@ -2,6 +2,7 @@ using RDMG.Core.Abstractions.Services.Exceptions; using RDMG.Core.Abstractions.Services.Models; using RDMG.Core.Generator; +using RDMG.Infrastructure.Data; using Shouldly; using Xunit; @@ -83,7 +84,7 @@ public async Task CreateOrUpdateDungeonAsync_WithValidNewModel_CreateOptionAndRe { var optionsModel = new DungeonOptionModel { - DungeonName = "UT Dungeon 2", + DungeonName = AppDbContextInitializer.UtDungeonName2, Created = DateTime.UtcNow, ItemsRarity = 1, DeadEnd = true, diff --git a/tests/RDMG.Core.Tests/RDMG.Core.Tests.csproj b/tests/RDMG.Core.Tests/RDMG.Core.Tests.csproj index a60ffcc..ff3110d 100644 --- a/tests/RDMG.Core.Tests/RDMG.Core.Tests.csproj +++ b/tests/RDMG.Core.Tests/RDMG.Core.Tests.csproj @@ -15,7 +15,6 @@ - From 1eeca28508254cf80d1ef7755abbb77a27919148 Mon Sep 17 00:00:00 2001 From: Externius Date: Sun, 27 Jul 2025 11:56:18 +0200 Subject: [PATCH 2/2] Use collection expression --- src/RDMG.Core/Generator/DungeonHelper.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/RDMG.Core/Generator/DungeonHelper.cs b/src/RDMG.Core/Generator/DungeonHelper.cs index ea01cd2..c6b5c9e 100644 --- a/src/RDMG.Core/Generator/DungeonHelper.cs +++ b/src/RDMG.Core/Generator/DungeonHelper.cs @@ -143,15 +143,20 @@ private List GetMonsters(IEnumerable monsters) { if (MonsterType.Equals("any", StringComparison.OrdinalIgnoreCase)) { - return monsters.Where(monster => Parse(monster.ChallengeRating) <= PartyLevel + 2 && + return + [ + .. monsters.Where(monster => Parse(monster.ChallengeRating) <= PartyLevel + 2 && Parse(monster.ChallengeRating) >= PartyLevel / 4.0) - .ToList(); + ]; } - return monsters.Where(monster => Parse(monster.ChallengeRating) <= PartyLevel + 2 && + return + [ + .. monsters.Where(monster => Parse(monster.ChallengeRating) <= PartyLevel + 2 && Parse(monster.ChallengeRating) >= PartyLevel / 4.0 && - MonsterType.Contains(monster.Type)) - .ToList(); + MonsterType.Contains(monster.Type, + StringComparison.InvariantCultureIgnoreCase)) + ]; } public string GetMonsterDescription()