From 0629f7c4c979ec827c943438d9d136f4ae18cfaf Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Mon, 15 Apr 2024 17:29:20 +0800 Subject: [PATCH] Introducing IAppInfrastructureService --- .../Core/Collection/TwoEnumerbleEnumerator.cs | 32 +++ .../ExceptionService/HutaoExceptionKind.cs | 1 + .../Model/Entity/Abstraction/IAppDbEntity.cs | 9 + .../Snap.Hutao/Model/Entity/Achievement.cs | 4 +- .../AppDbServiceAppDbEntityExtension.cs | 22 ++ .../Abstraction/AppDbServiceExtension.cs | 120 ++++++++++ .../Service/Abstraction/DbStoreOptions.cs | 66 +----- .../Service/Abstraction/IAppDbService.cs | 9 + .../Abstraction/IAppInfrastructureService.cs | 9 + .../Service/Abstraction/IAppService.cs | 6 + .../Abstraction/ServiceScopeExtension.cs | 27 +++ .../Achievement/AchievementDbBulkOperation.cs | 222 +++++++----------- .../Achievement/AchievementDbService.cs | 114 +++------ .../Achievement/IAchievementDbService.cs | 3 +- .../{ => Announcement}/AnnouncementService.cs | 2 +- .../IAnnouncementService.cs | 2 +- .../ViewModel/Home/AnnouncementViewModel.cs | 2 +- 17 files changed, 364 insertions(+), 286 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Collection/TwoEnumerbleEnumerator.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IAppDbEntity.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceAppDbEntityExtension.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceExtension.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppInfrastructureService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/ServiceScopeExtension.cs rename src/Snap.Hutao/Snap.Hutao/Service/{ => Announcement}/AnnouncementService.cs (99%) rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => Announcement}/IAnnouncementService.cs (94%) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Collection/TwoEnumerbleEnumerator.cs b/src/Snap.Hutao/Snap.Hutao/Core/Collection/TwoEnumerbleEnumerator.cs new file mode 100644 index 00000000..f88ad041 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Collection/TwoEnumerbleEnumerator.cs @@ -0,0 +1,32 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Collection; + +internal sealed class TwoEnumerbleEnumerator : IDisposable +{ + private readonly IEnumerator firstEnumerator; + private readonly IEnumerator secondEnumerator; + + public TwoEnumerbleEnumerator(IEnumerable firstEnumerable, IEnumerable secondEnumerable) + { + firstEnumerator = firstEnumerable.GetEnumerator(); + secondEnumerator = secondEnumerable.GetEnumerator(); + } + + public (TFirst First, TSecond Second) Current { get => (firstEnumerator.Current, secondEnumerator.Current); } + + public bool MoveNext(ref bool moveFirst, ref bool moveSecond) + { + moveFirst = moveFirst && firstEnumerator.MoveNext(); + moveSecond = moveSecond && secondEnumerator.MoveNext(); + + return moveFirst || moveSecond; + } + + public void Dispose() + { + firstEnumerator.Dispose(); + secondEnumerator.Dispose(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs index 609216d4..c7e3c6d4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs @@ -9,6 +9,7 @@ internal enum HutaoExceptionKind // Foundation ImageCacheInvalidUri, + DatabaseCorrupted, // IO FileSystemCreateFileInsufficientPermissions, diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IAppDbEntity.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IAppDbEntity.cs new file mode 100644 index 00000000..3e90f714 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Abstraction/IAppDbEntity.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Entity.Abstraction; + +internal interface IAppDbEntity +{ + Guid InnerId { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs index f93f4eb2..0c587661 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Achievement.cs @@ -16,8 +16,8 @@ namespace Snap.Hutao.Model.Entity; [HighQuality] [Table("achievements")] [SuppressMessage("", "SA1124")] -internal sealed class Achievement - : IEquatable, +internal sealed class Achievement : IAppDbEntity, + IEquatable, IDbMappingForeignKeyFrom, IDbMappingForeignKeyFrom { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceAppDbEntityExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceAppDbEntityExtension.cs new file mode 100644 index 00000000..c412fc0b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceAppDbEntityExtension.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Entity.Abstraction; + +namespace Snap.Hutao.Service.Abstraction; + +internal static class AppDbServiceAppDbEntityExtension +{ + public static int DeleteByInnerId(this IAppDbService service, TEntity entity) + where TEntity : class, IAppDbEntity + { + return service.Execute(dbset => dbset.ExecuteDeleteWhere(e => e.InnerId == entity.InnerId)); + } + + public static ValueTask DeleteByInnerIdAsync(this IAppDbService service, TEntity entity) + where TEntity : class, IAppDbEntity + { + return service.ExecuteAsync(dbset => dbset.ExecuteDeleteWhereAsync(e => e.InnerId == entity.InnerId)); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceExtension.cs new file mode 100644 index 00000000..6eef72e4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/AppDbServiceExtension.cs @@ -0,0 +1,120 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Entity.Database; +using System.Linq.Expressions; + +namespace Snap.Hutao.Service.Abstraction; + +internal static class AppDbServiceExtension +{ + public static TResult Execute(this IAppDbService service, Func, TResult> func) + where TEntity : class + { + using (IServiceScope scope = service.ServiceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.GetAppDbContext(); + return func(appDbContext.Set()); + } + } + + public static async ValueTask ExecuteAsync(this IAppDbService service, Func, ValueTask> asyncFunc) + where TEntity : class + { + using (IServiceScope scope = service.ServiceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.GetAppDbContext(); + return await asyncFunc(appDbContext.Set()).ConfigureAwait(false); + } + } + + public static async ValueTask ExecuteAsync(this IAppDbService service, Func, Task> asyncFunc) + where TEntity : class + { + using (IServiceScope scope = service.ServiceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.GetAppDbContext(); + return await asyncFunc(appDbContext.Set()).ConfigureAwait(false); + } + } + + public static int Add(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.Execute(dbset => dbset.AddAndSave(entity)); + } + + public static ValueTask AddAsync(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.ExecuteAsync(dbset => dbset.AddAndSaveAsync(entity)); + } + + public static int AddRange(this IAppDbService service, IEnumerable entities) + where TEntity : class + { + return service.Execute(dbset => dbset.AddRangeAndSave(entities)); + } + + public static ValueTask AddRangeAsync(this IAppDbService service, IEnumerable entities) + where TEntity : class + { + return service.ExecuteAsync(dbset => dbset.AddRangeAndSaveAsync(entities)); + } + + public static TEntity Single(this IAppDbService service, Expression> predicate) + where TEntity : class + { + return service.Execute(dbset => dbset.AsNoTracking().Single(predicate)); + } + + public static ValueTask SingleAsync(this IAppDbService service, Expression> predicate) + where TEntity : class + { + return service.ExecuteAsync(dbset => dbset.AsNoTracking().SingleAsync(predicate, default)); + } + + public static TResult Query(this IAppDbService service, Func, TResult> func) + where TEntity : class + { + return service.Execute(dbset => func(dbset.AsNoTracking())); + } + + public static ValueTask QueryAsync(this IAppDbService service, Func, ValueTask> func) + where TEntity : class + { + return service.ExecuteAsync(dbset => func(dbset.AsNoTracking())); + } + + public static ValueTask QueryAsync(this IAppDbService service, Func, Task> func) + where TEntity : class + { + return service.ExecuteAsync(dbset => func(dbset.AsNoTracking())); + } + + public static int Update(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.Execute(dbset => dbset.UpdateAndSave(entity)); + } + + public static ValueTask UpdateAsync(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.ExecuteAsync(dbset => dbset.UpdateAndSaveAsync(entity)); + } + + public static int Delete(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.Execute(dbset => dbset.RemoveAndSave(entity)); + } + + public static ValueTask DeleteAsync(this IAppDbService service, TEntity entity) + where TEntity : class + { + return service.ExecuteAsync(dbset => dbset.RemoveAndSaveAsync(entity)); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs index 56167c1b..d3ef0c1e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs @@ -14,20 +14,10 @@ namespace Snap.Hutao.Service.Abstraction; /// 数据库存储选项的设置 /// [ConstructorGenerated] -internal abstract partial class DbStoreOptions : ObservableObject, IOptions +internal abstract partial class DbStoreOptions : ObservableObject { private readonly IServiceProvider serviceProvider; - /// - public DbStoreOptions Value { get => this; } - - /// - /// 从数据库中获取字符串数据 - /// - /// 存储字段 - /// 键 - /// 默认值 - /// protected string GetOption(ref string? storage, string key, string defaultValue = "") { return GetOption(ref storage, key, () => defaultValue); @@ -49,13 +39,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 从数据库中获取bool数据 - /// - /// 存储字段 - /// 键 - /// 默认值 - /// protected bool GetOption(ref bool? storage, string key, bool defaultValue = false) { return GetOption(ref storage, key, () => defaultValue); @@ -78,13 +61,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 从数据库中获取int数据 - /// - /// 存储字段 - /// 键 - /// 默认值 - /// protected int GetOption(ref int? storage, string key, int defaultValue = 0) { return GetOption(ref storage, key, () => defaultValue); @@ -107,15 +83,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 从数据库中获取任何类型的数据 - /// - /// 数据的类型 - /// 存储字段 - /// 键 - /// 反序列化器 - /// 默认值 - /// [return: NotNull] protected T GetOption(ref T? storage, string key, Func deserializer, [DisallowNull] T defaultValue) { @@ -160,13 +127,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 将值存入数据库 - /// - /// 存储字段 - /// 键 - /// 值 - /// 属性名称 protected void SetOption(ref string? storage, string key, string? value, [CallerMemberName] string? propertyName = null) { if (!SetProperty(ref storage, value, propertyName)) @@ -182,14 +142,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 将值存入数据库 - /// - /// 存储字段 - /// 键 - /// 值 - /// 属性名称 - /// 是否设置了值 protected bool SetOption(ref bool? storage, string key, bool value, [CallerMemberName] string? propertyName = null) { bool set = SetProperty(ref storage, value, propertyName); @@ -208,13 +160,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 将值存入数据库 - /// - /// 存储字段 - /// 键 - /// 值 - /// 属性名称 protected void SetOption(ref int? storage, string key, int value, [CallerMemberName] string? propertyName = null) { if (!SetProperty(ref storage, value, propertyName)) @@ -230,15 +175,6 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions - /// 将值存入数据库 - /// - /// 数据的类型 - /// 存储字段 - /// 键 - /// 值 - /// 序列化器 - /// 属性名称 protected void SetOption(ref T? storage, string key, T value, Func serializer, [CallerMemberName] string? propertyName = null) { if (!SetProperty(ref storage, value, propertyName)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppDbService.cs new file mode 100644 index 00000000..d308b0e4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppDbService.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Abstraction; + +internal interface IAppDbService : IAppInfrastructureService + where TEntity : class +{ +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppInfrastructureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppInfrastructureService.cs new file mode 100644 index 00000000..0cff36dd --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppInfrastructureService.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Abstraction; + +internal interface IAppInfrastructureService +{ + IServiceProvider ServiceProvider { get; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppService.cs new file mode 100644 index 00000000..a7eeb9bf --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAppService.cs @@ -0,0 +1,6 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Abstraction; + +internal interface IAppService; \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/ServiceScopeExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/ServiceScopeExtension.cs new file mode 100644 index 00000000..0f8a00d8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/ServiceScopeExtension.cs @@ -0,0 +1,27 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Model.Entity.Database; + +namespace Snap.Hutao.Service.Abstraction; + +internal static class ServiceScopeExtension +{ + public static TService GetRequiredService(this IServiceScope scope) + where TService : class + { + return scope.ServiceProvider.GetRequiredService(); + } + + public static TDbContext GetDbContext(this IServiceScope scope) + where TDbContext : DbContext + { + return scope.GetRequiredService(); + } + + public static AppDbContext GetAppDbContext(this IServiceScope scope) + { + return scope.GetDbContext(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbBulkOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbBulkOperation.cs index 92d6195e..e99d97fb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbBulkOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbBulkOperation.cs @@ -2,9 +2,11 @@ // Licensed under the MIT license. using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Core.Collection; using Snap.Hutao.Core.Database; using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Model.InterChange.Achievement; +using Snap.Hutao.Service.Abstraction; using EntityAchievement = Snap.Hutao.Model.Entity.Achievement; namespace Snap.Hutao.Service.Achievement; @@ -21,197 +23,155 @@ internal sealed partial class AchievementDbBulkOperation private readonly IServiceProvider serviceProvider; private readonly ILogger logger; - /// - /// 合并 - /// - /// 成就id - /// 待合并的项 - /// 是否贪婪 - /// 导入结果 public ImportResult Merge(Guid archiveId, IEnumerable items, bool aggressive) { - logger.LogInformation("Perform {Method} Operation for archive: {Id}, Aggressive: {Aggressive}", nameof(Merge), archiveId, aggressive); + logger.LogInformation("Perform merge operation for [Archive: {Id}], [Aggressive: {Aggressive}]", archiveId, aggressive); using (IServiceScope scope = serviceProvider.CreateScope()) { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + AppDbContext appDbContext = scope.GetAppDbContext(); IOrderedQueryable oldData = appDbContext.Achievements .AsNoTracking() .Where(a => a.ArchiveId == archiveId) .OrderBy(a => a.Id); - int add = 0; - int update = 0; + (int add, int update) = (0, 0); - using (IEnumerator entityEnumerator = oldData.GetEnumerator()) + using (TwoEnumerbleEnumerator enumerator = new(oldData, items)) { - using (IEnumerator uiafEnumerator = items.GetEnumerator()) + (bool moveEntity, bool moveUIAF) = (true, true); + + while (true) { - bool moveEntity = true; - bool moveUIAF = true; - - while (true) + if (!enumerator.MoveNext(ref moveEntity, ref moveUIAF)) { - bool moveEntityResult = moveEntity && entityEnumerator.MoveNext(); - bool moveUIAFResult = moveUIAF && uiafEnumerator.MoveNext(); + break; + } - if (!(moveEntityResult || moveUIAFResult)) - { - break; - } - else - { - EntityAchievement? entity = entityEnumerator.Current; - UIAFItem? uiaf = uiafEnumerator.Current; - - if (entity is null && uiaf is not null) - { - appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf)); - add++; - continue; - } - else if (entity is not null && uiaf is null) - { - // skip - continue; - } + (EntityAchievement? entity, UIAFItem? uiaf) = enumerator.Current; + switch (entity, uiaf) + { + case (null, not null): + appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf)); + add++; + continue; + case (not null, null): + continue; // Skipped + default: ArgumentNullException.ThrowIfNull(entity); ArgumentNullException.ThrowIfNull(uiaf); - if (entity.Id < uiaf.Id) + switch (entity.Id.CompareTo(uiaf.Id)) { - moveEntity = true; - moveUIAF = false; - } - else if (entity.Id == uiaf.Id) - { - moveEntity = true; - moveUIAF = true; + case < 0: + (moveEntity, moveUIAF) = (true, false); + break; + case 0: + (moveEntity, moveUIAF) = (true, true); + + if (aggressive) + { + appDbContext.Achievements.RemoveAndSave(entity); + appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf)); + update++; + } + + break; + case > 0: + (moveEntity, moveUIAF) = (false, true); - if (aggressive) - { - appDbContext.Achievements.RemoveAndSave(entity); appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf)); - update++; - } + add++; + break; } - else - { - // entity.Id > uiaf.Id - moveEntity = false; - moveUIAF = true; - appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf)); - add++; - } - } + break; } } } - logger.LogInformation("{Method} Operation Complete, Add: {Add}, Update: {Update}", nameof(Merge), add, update); + logger.LogInformation("Merge operation complete, [Add: {Add}], [Update: {Update}]", add, update); return new(add, update, 0); } } - /// - /// 覆盖 - /// - /// 成就id - /// 待覆盖的项 - /// 导入结果 public ImportResult Overwrite(Guid archiveId, IEnumerable items) { - logger.LogInformation("Perform {Method} Operation for archive: {Id}", nameof(Overwrite), archiveId); + logger.LogInformation("Perform Overwrite Operation for [Archive: {Id}]", archiveId); using (IServiceScope scope = serviceProvider.CreateScope()) { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + AppDbContext appDbContext = scope.GetAppDbContext(); IOrderedQueryable oldData = appDbContext.Achievements .AsNoTracking() .Where(a => a.ArchiveId == archiveId) .OrderBy(a => a.Id); - int add = 0; - int update = 0; - int remove = 0; + (int add, int update, int remove) = (0, 0, 0); - using (IEnumerator oldDataEnumerator = oldData.GetEnumerator()) + using (TwoEnumerbleEnumerator enumerator = new(oldData, items)) { - using (IEnumerator newDataEnumerator = items.GetEnumerator()) + (bool moveOld, bool moveNew) = (true, true); + + while (true) { - bool moveOld = true; - bool moveNew = true; - - while (true) + if (!enumerator.MoveNext(ref moveOld, ref moveNew)) { - bool moveOldResult = moveOld && oldDataEnumerator.MoveNext(); - bool moveNewResult = moveNew && newDataEnumerator.MoveNext(); + break; + } - if (moveOldResult || moveNewResult) - { - EntityAchievement? oldEntity = oldDataEnumerator.Current; - EntityAchievement? newEntity = newDataEnumerator.Current; - - if (oldEntity is null && newEntity is not null) - { - appDbContext.Achievements.AddAndSave(newEntity); - add++; - continue; - } - else if (oldEntity is not null && newEntity is null) - { - appDbContext.Achievements.RemoveAndSave(oldEntity); - remove++; - continue; - } + (EntityAchievement? oldEntity, EntityAchievement? newEntity) = enumerator.Current; + switch (oldEntity, newEntity) + { + case (null, not null): + appDbContext.Achievements.AddAndSave(newEntity); + add++; + continue; + case (not null, null): + appDbContext.Achievements.RemoveAndSave(oldEntity); + remove++; + continue; + default: ArgumentNullException.ThrowIfNull(oldEntity); ArgumentNullException.ThrowIfNull(newEntity); - if (oldEntity.Id < newEntity.Id) + switch (oldEntity.Id.CompareTo(newEntity.Id)) { - moveOld = true; - moveNew = false; - appDbContext.Achievements.RemoveAndSave(oldEntity); - remove++; - } - else if (oldEntity.Id == newEntity.Id) - { - moveOld = true; - moveNew = true; + case < 0: + (moveOld, moveNew) = (true, false); + break; + case 0: + (moveOld, moveNew) = (true, true); + + if (oldEntity.Equals(newEntity)) + { + // Skip same entry, reduce write operation. + continue; + } + else + { + appDbContext.Achievements.RemoveAndSave(oldEntity); + appDbContext.Achievements.AddAndSave(newEntity); + update++; + } + + break; + case > 0: + (moveOld, moveNew) = (false, true); - if (oldEntity.Equals(newEntity)) - { - // skip same entry. - continue; - } - else - { - appDbContext.Achievements.RemoveAndSave(oldEntity); appDbContext.Achievements.AddAndSave(newEntity); - update++; - } + add++; + break; } - else - { - // entity.Id > uiaf.Id - moveOld = false; - moveNew = true; - appDbContext.Achievements.AddAndSave(newEntity); - add++; - } - } - else - { + break; - } } } } - logger.LogInformation("{Method} Operation Complete, Add: {Add}, Update: {Update}, Remove: {Remove}", nameof(Overwrite), add, update, remove); + logger.LogInformation("Overwrite Operation Complete, Add: {Add}, Update: {Update}, Remove: {Remove}", add, update, remove); return new(add, update, remove); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs index 8440a8b9..bdfab117 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs @@ -7,6 +7,8 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Abstraction; +using Snap.Hutao.Web.Request.Builder; using System.Collections.ObjectModel; using EntityAchievement = Snap.Hutao.Model.Entity.Achievement; @@ -21,148 +23,92 @@ internal sealed partial class AchievementDbService : IAchievementDbService { private readonly IServiceProvider serviceProvider; + public IServiceProvider ServiceProvider { get => serviceProvider; } + public Dictionary GetAchievementMapByArchiveId(Guid archiveId) { - Dictionary entities; try { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - entities = appDbContext.Achievements - .AsNoTracking() - .Where(a => a.ArchiveId == archiveId) - .ToDictionary(a => (AchievementId)a.Id); - } + return this.Query>(query => query + .Where(a => a.ArchiveId == archiveId) + .ToDictionary(a => (AchievementId)a.Id)); } catch (ArgumentException ex) { - throw ThrowHelper.DatabaseCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex); + throw HutaoException.Throw(HutaoExceptionKind.DatabaseCorrupted, SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex); } - - return entities; } public async ValueTask GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return await appDbContext.Achievements - .AsNoTracking() + return await this.QueryAsync(query => query .Where(a => a.ArchiveId == archiveId) .Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) - .CountAsync() - .ConfigureAwait(false); - } + .CountAsync()) + .ConfigureAwait(false); } [SuppressMessage("", "CA1305")] public async ValueTask> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return await appDbContext.Achievements - .AsNoTracking() + return await this.QueryAsync>(query => query .Where(a => a.ArchiveId == archiveId) .Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) .OrderByDescending(a => a.Time.ToString()) .Take(take) - .ToListAsync() - .ConfigureAwait(false); - } + .ToListAsync()) + .ConfigureAwait(false); } public void OverwriteAchievement(EntityAchievement achievement) { - using (IServiceScope scope = serviceProvider.CreateScope()) + this.DeleteByInnerId(achievement); + if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - // Delete exists one. - appDbContext.Achievements.ExecuteDeleteWhere(e => e.InnerId == achievement.InnerId); - if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) - { - appDbContext.Achievements.AddAndSave(achievement); - } + this.Add(achievement); } } public async ValueTask OverwriteAchievementAsync(EntityAchievement achievement) { - using (IServiceScope scope = serviceProvider.CreateScope()) + await this.DeleteByInnerIdAsync(achievement).ConfigureAwait(false); + if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - // Delete exists one. - await appDbContext.Achievements.ExecuteDeleteWhereAsync(e => e.InnerId == achievement.InnerId).ConfigureAwait(false); - if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED) - { - await appDbContext.Achievements.AddAndSaveAsync(achievement).ConfigureAwait(false); - } + await this.AddAsync(achievement).ConfigureAwait(false); } } public ObservableCollection GetAchievementArchiveCollection() { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.AchievementArchives.AsNoTracking().ToObservableCollection(); - } + return this.Query>(query => query.ToObservableCollection()); } public async ValueTask RemoveAchievementArchiveAsync(AchievementArchive archive) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - // It will cascade deleted the achievements. - await appDbContext.AchievementArchives.RemoveAndSaveAsync(archive).ConfigureAwait(false); - } + // It will cascade deleted the achievements. + await this.DeleteAsync(archive).ConfigureAwait(false); } public List GetAchievementListByArchiveId(Guid archiveId) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - IQueryable result = appDbContext.Achievements.AsNoTracking().Where(i => i.ArchiveId == archiveId); - return [.. result]; - } + return this.Query>(query => [.. query.Where(a => a.ArchiveId == archiveId)]); } public async ValueTask> GetAchievementListByArchiveIdAsync(Guid archiveId) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return await appDbContext.Achievements - .AsNoTracking() - .Where(i => i.ArchiveId == archiveId) - .ToListAsync() - .ConfigureAwait(false); - } + return await this.QueryAsync>(query => query + .Where(a => a.ArchiveId == archiveId) + .ToListAsync()) + .ConfigureAwait(false); } public List GetAchievementArchiveList() { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - IQueryable result = appDbContext.AchievementArchives.AsNoTracking(); - return [.. result]; - } + return this.Query>(query => [.. query]); } public async ValueTask> GetAchievementArchiveListAsync() { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return await appDbContext.AchievementArchives.AsNoTracking().ToListAsync().ConfigureAwait(false); - } + return await this.QueryAsync>(query => query.ToListAsync()).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs index ebb177df..6e33e94a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementDbService.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Abstraction; using System.Collections.ObjectModel; using EntityAchievement = Snap.Hutao.Model.Entity.Achievement; namespace Snap.Hutao.Service.Achievement; -internal interface IAchievementDbService +internal interface IAchievementDbService : IAppDbService, IAppDbService { ValueTask RemoveAchievementArchiveAsync(Model.Entity.AchievementArchive archive); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs similarity index 99% rename from src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs index e31cfe17..dab0fa50 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Caching.Memory; using Snap.Hutao.Core; -using Snap.Hutao.Service.Abstraction; +using Snap.Hutao.Service.Announcement; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement; using Snap.Hutao.Web.Response; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/IAnnouncementService.cs similarity index 94% rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAnnouncementService.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Announcement/IAnnouncementService.cs index a6754d27..9dc59681 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IAnnouncementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/IAnnouncementService.cs @@ -4,7 +4,7 @@ using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement; -namespace Snap.Hutao.Service.Abstraction; +namespace Snap.Hutao.Service.Announcement; /// /// 公告服务 diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs index df017b4a..d91a1820 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs @@ -3,7 +3,7 @@ using Snap.Hutao.Core.Setting; using Snap.Hutao.Service; -using Snap.Hutao.Service.Abstraction; +using Snap.Hutao.Service.Announcement; using Snap.Hutao.Service.Hutao; using Snap.Hutao.View.Card; using Snap.Hutao.View.Card.Primitive;