diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs index 9dc87951..9fcfb952 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs @@ -22,6 +22,7 @@ public class InjectionGenerator : ISourceGenerator { private const string InjectAsSingletonName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Singleton"; private const string InjectAsTransientName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Transient"; + private const string InjectAsScopedName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Scoped"; /// public void Initialize(GeneratorInitializationContext context) @@ -97,8 +98,11 @@ internal static partial class ServiceCollectionExtensions case InjectAsTransientName: lineBuilder.Append(@" services.AddTransient("); break; + case InjectAsScopedName: + lineBuilder.Append(@" services.AddScoped("); + break; default: - throw new InvalidOperationException($"非法的InjectAs值: [{injectAsName}]"); + throw new InvalidOperationException($"非法的 InjectAs 值: [{injectAsName}]"); } if (arguments.Length == 2) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs index 88b6d70b..841049cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs @@ -28,9 +28,6 @@ public class ScopedPage : Page serviceScope = Ioc.Default.CreateScope(); } - /// - public IServiceProvider ServiceProvider { get => serviceScope.ServiceProvider; } - /// /// 初始化 /// @@ -38,7 +35,7 @@ public class ScopedPage : Page public void InitializeWith() where TViewModel : class, ISupportCancellation { - ISupportCancellation viewModel = ServiceProvider.GetRequiredService(); + ISupportCancellation viewModel = serviceScope.ServiceProvider.GetRequiredService(); viewModel.CancellationToken = viewLoadingCancellationTokenSource.Token; DataContext = viewModel; } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs index c02db516..e2ce01a9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs @@ -23,6 +23,8 @@ public abstract class CacheBase { private readonly SemaphoreSlim cacheFolderSemaphore = new(1); private readonly ILogger logger; + + // violate di rule private readonly HttpClient httpClient; private StorageFolder? baseFolder; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbCurrent.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbCurrent.cs index bb05fa6d..bc3add7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbCurrent.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbCurrent.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.EntityFrameworkCore; -using Snap.Hutao.Extension; namespace Snap.Hutao.Core.Database; @@ -17,7 +16,6 @@ internal class DbCurrent where TEntity : class, ISelectable where TMessage : Message.ValueChangedMessage, new() { - private readonly DbContext dbContext; private readonly DbSet dbSet; private readonly IMessenger messenger; @@ -31,7 +29,6 @@ internal class DbCurrent /// public DbCurrent(DbSet dbSet, IMessenger messenger) { - this.dbContext = dbSet.Context(); this.dbSet = dbSet; this.messenger = messenger; } diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/DbSetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs similarity index 67% rename from src/Snap.Hutao/Snap.Hutao/Extension/DbSetExtension.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs index aac80a60..e0e87e8f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/DbSetExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -namespace Snap.Hutao.Extension; +namespace Snap.Hutao.Core.Database; /// /// 数据库集合上下文 @@ -50,6 +50,34 @@ public static class DbSetExtension return entry; } + /// + /// 添加并保存 + /// + /// 实体类型 + /// 数据库集 + /// 实体 + /// 影响条数 + public static int AddAndSave(this DbSet dbSet, TEntity entity) + where TEntity : class + { + dbSet.Add(entity); + return dbSet.Context().SaveChanges(); + } + + /// + /// 移除并保存 + /// + /// 实体类型 + /// 数据库集 + /// 实体 + /// 影响条数 + public static int RemoveAndSave(this DbSet dbSet, TEntity entity) + where TEntity : class + { + dbSet.Remove(entity); + return dbSet.Context().SaveChanges(); + } + /// /// 更新并保存 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectAs.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectAs.cs index a9f36cfc..c970b34c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectAs.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectAs.cs @@ -17,4 +17,9 @@ public enum InjectAs /// 指示应注册为短期对象 /// Transient, + + /// + /// 指示应注册为范围对象 + /// + Scoped, } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs index 14bef433..40142bb0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs @@ -21,4 +21,4 @@ internal class SeparatorCommaInt32EnumerableConverter : JsonConverter internal static class SettingKeys { - /// - /// 上次打开时App的版本 - /// - public const string LastAppVersion = "LastAppVersion"; - /// /// 窗体左侧 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs index 09a31df1..10f37bf9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs @@ -38,18 +38,6 @@ public static class Must } } - /// - /// 任务异常 - /// - /// 异常消息 - /// 异常的任务 - [SuppressMessage("", "VSTHRD200")] - public static Task Fault(string message) - { - InvalidOperationException exception = new(message); - return Task.FromException(exception); - } - /// /// 任务异常 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs index 9d7ffa72..d0c3e409 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs @@ -1,12 +1,14 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.ComponentModel; + namespace Snap.Hutao.Model.Binding; /// /// 用于视图绑定的成就 /// -public class Achievement : Observable +public class Achievement : ObservableObject { /// /// 满进度占位符 @@ -28,7 +30,7 @@ public class Achievement : Observable this.inner = inner; this.entity = entity; - // Property should only be set when is user checking. + // Property should only be set when it's user checking. isChecked = (int)entity.Status >= 2; } @@ -50,7 +52,7 @@ public class Achievement : Observable get => isChecked; set { - Set(ref isChecked, value); + SetProperty(ref isChecked, value); // Only update state when checked if (value) @@ -67,6 +69,6 @@ public class Achievement : Observable /// public string Time { - get => entity.Time.ToString("yyyy-MM-dd HH:mm:ss"); + get => entity.Time.ToString("yyyy.MM.dd HH:mm:ss"); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs index 5fcd09d8..1df4de43 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Extension; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Bbs.User; @@ -12,7 +13,7 @@ namespace Snap.Hutao.Model.Binding; /// /// 用于视图绑定的用户 /// -public class User : Observable +public class User : ObservableObject { private readonly EntityUser inner; @@ -44,7 +45,7 @@ public class User : Observable public UserGameRole? SelectedUserGameRole { get => selectedUserGameRole; - private set => Set(ref selectedUserGameRole, value); + private set => SetProperty(ref selectedUserGameRole, value); } /// @@ -58,7 +59,19 @@ public class User : Observable public Cookie Cookie { get => inner.Cookie; - set => inner.Cookie = value; + set + { + inner.Cookie = value; + OnPropertyChanged(nameof(HasSToken)); + } + } + + /// + /// 是否拥有 SToken + /// + public bool HasSToken + { + get => inner.Cookie.ContainsSToken(); } /// @@ -71,6 +84,17 @@ public class User : Observable /// public bool IsInitialized { get => isInitialized; } + /// + /// 更新SToken + /// + /// uid + /// cookie + internal void UpdateSToken(string uid, Cookie cookie) + { + Cookie.InsertSToken(uid, cookie); + OnPropertyChanged(nameof(HasSToken)); + } + /// /// 从数据库恢复用户 /// @@ -79,11 +103,7 @@ public class User : Observable /// 角色客户端 /// 取消令牌 /// 用户是否初始化完成,若Cookie失效会返回 - internal static async Task ResumeAsync( - EntityUser inner, - UserClient userClient, - BindingClient userGameRoleClient, - CancellationToken token = default) + internal static async Task ResumeAsync(EntityUser inner, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) { User user = new(inner); bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false); @@ -98,36 +118,20 @@ public class User : Observable /// 角色客户端 /// 取消令牌 /// 用户是否初始化完成,若Cookie失效会返回 - internal static async Task CreateAsync( - Cookie cookie, - UserClient userClient, - BindingClient userGameRoleClient, - CancellationToken token = default) + internal static async Task CreateAsync(Cookie cookie, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) { User user = new(EntityUser.Create(cookie)); bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false); return successful ? user : null; } - private async Task InitializeCoreAsync( - UserClient userClient, - BindingClient userGameRoleClient, - CancellationToken token = default) + private async Task InitializeCoreAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) { if (isInitialized) { return true; } - await InitializeUserInfoAndUserGameRolesAsync(userClient, userGameRoleClient, token).ConfigureAwait(false); - - isInitialized = true; - - return UserInfo != null && UserGameRoles.Any(); - } - - private async Task InitializeUserInfoAndUserGameRolesAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token) - { UserInfo = await userClient .GetUserFullInfoAsync(this, token) .ConfigureAwait(false); @@ -137,5 +141,9 @@ public class User : Observable .ConfigureAwait(false); SelectedUserGameRole = UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen); + + isInitialized = true; + + return UserInfo != null && UserGameRoles.Any(); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Observable.cs b/src/Snap.Hutao/Snap.Hutao/Model/Observable.cs deleted file mode 100644 index 9b0db94f..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Observable.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using System.Runtime.CompilerServices; - -namespace Snap.Hutao.Model; - -/// -/// 简单的实现了 接口 -/// -public abstract class Observable : INotifyPropertyChanged -{ - /// - public event PropertyChangedEventHandler? PropertyChanged; - - /// - /// 设置字段的值 - /// - /// 字段类型 - /// 现有值 - /// 新的值 - /// 属性名称 - /// 项是否更新 - protected bool Set([NotNullIfNotNull("value")] ref T storage, T value, [CallerMemberName] string propertyName = default!) - { - if (Equals(storage, value)) - { - return false; - } - - storage = value; - OnPropertyChanged(propertyName); - return true; - } - - /// - /// 触发 - /// - /// 属性名称 - protected void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs new file mode 100644 index 00000000..811d5e61 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs @@ -0,0 +1,65 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive.Converter; + +namespace Snap.Hutao.Model.Primitive; + +/// +/// 角色Id +/// +[JsonConverter(typeof(AvatarIdConverter))] +public readonly struct AvatarId : IEquatable +{ + /// + /// 值 + /// + public readonly int Value; + + /// + /// Initializes a new instance of the struct. + /// + /// value + public AvatarId(int value) + { + Value = value; + } + + public static implicit operator int(AvatarId value) + { + return value.Value; + } + + public static implicit operator AvatarId(int value) + { + return new(value); + } + + public static bool operator ==(AvatarId left, AvatarId right) + { + return left.Value == right.Value; + } + + public static bool operator !=(AvatarId left, AvatarId right) + { + return !(left == right); + } + + /// + public bool Equals(AvatarId other) + { + return Value == other.Value; + } + + /// + public override bool Equals(object? obj) + { + return obj is AvatarId other && Equals(other); + } + + /// + public override int GetHashCode() + { + return Value.GetHashCode(); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/AvatarIdConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/AvatarIdConverter.cs new file mode 100644 index 00000000..4d1f9443 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/AvatarIdConverter.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Primitive.Converter; + +/// +/// 角色Id转换器 +/// +internal class AvatarIdConverter : JsonConverter +{ + /// + public override AvatarId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.GetInt32(); + } + + /// + public override void Write(Utf8JsonWriter writer, AvatarId value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/WeaponIdConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/WeaponIdConverter.cs new file mode 100644 index 00000000..e489f3a2 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/Converter/WeaponIdConverter.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Primitive.Converter; + +/// +/// 武器Id转换器 +/// +internal class WeaponIdConverter : JsonConverter +{ + /// + public override WeaponId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.GetInt32(); + } + + /// + public override void Write(Utf8JsonWriter writer, WeaponId value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs new file mode 100644 index 00000000..731234de --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs @@ -0,0 +1,65 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive.Converter; + +namespace Snap.Hutao.Model.Primitive; + +/// +/// 武器Id +/// +[JsonConverter(typeof(WeaponIdConverter))] +public readonly struct WeaponId : IEquatable +{ + /// + /// 值 + /// + public readonly int Value; + + /// + /// Initializes a new instance of the struct. + /// + /// value + public WeaponId(int value) + { + Value = value; + } + + public static implicit operator int(WeaponId value) + { + return value.Value; + } + + public static implicit operator WeaponId(int value) + { + return new(value); + } + + public static bool operator ==(WeaponId left, WeaponId right) + { + return left.Value == right.Value; + } + + public static bool operator !=(WeaponId left, WeaponId right) + { + return !(left == right); + } + + /// + public bool Equals(WeaponId other) + { + return Value == other.Value; + } + + /// + public override bool Equals(object? obj) + { + return obj is WeaponId other && Equals(other); + } + + /// + public override int GetHashCode() + { + return Value.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs b/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs index c1ba5061..a3c12bfa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Selectable.cs @@ -1,6 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.ComponentModel; + namespace Snap.Hutao.Model; /// @@ -8,7 +10,7 @@ namespace Snap.Hutao.Model; /// 默认为选中状态 /// /// 值的类型 -public class Selectable : Observable +public class Selectable : ObservableObject where T : class { private readonly Action? selectedChanged; @@ -35,7 +37,7 @@ public class Selectable : Observable get => isSelected; set { - Set(ref isSelected, value); + SetProperty(ref isSelected, value); selectedChanged?.Invoke(); } } @@ -43,5 +45,5 @@ public class Selectable : Observable /// /// 存放的对象 /// - public T Value { get => value; set => Set(ref this.value, value); } + public T Value { get => value; set => SetProperty(ref this.value, value); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbOperation.cs index b29f75c5..7bbc1f8b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbOperation.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Context.Database; +using Snap.Hutao.Core.Database; using Snap.Hutao.Model.InterChange.Achievement; using EntityAchievement = Snap.Hutao.Model.Entity.Achievement; @@ -63,7 +64,7 @@ public class AchievementDbOperation if (entity == null && uiaf != null) { - AddEntity(EntityAchievement.Create(archiveId, uiaf)); + appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf)); add++; continue; } @@ -85,8 +86,8 @@ public class AchievementDbOperation if (aggressive) { - RemoveEntity(entity); - AddEntity(EntityAchievement.Create(archiveId, uiaf)); + appDbContext.Achievements.RemoveAndSave(entity); + appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf)); update++; } } @@ -96,7 +97,7 @@ public class AchievementDbOperation moveEntity = false; moveUIAF = true; - AddEntity(EntityAchievement.Create(archiveId, uiaf)); + appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf)); add++; } } @@ -115,7 +116,7 @@ public class AchievementDbOperation /// 导入结果 public ImportResult Overwrite(Guid archiveId, IEnumerable items) { - IQueryable oldData = appDbContext.Achievements + IOrderedQueryable oldData = appDbContext.Achievements .Where(a => a.ArchiveId == archiveId) .OrderBy(a => a.Id); @@ -142,13 +143,13 @@ public class AchievementDbOperation if (oldEntity == null && newEntity != null) { - AddEntity(newEntity); + appDbContext.Achievements.AddAndSave(newEntity); add++; continue; } else if (oldEntity != null && newEntity == null) { - RemoveEntity(oldEntity); + appDbContext.Achievements.RemoveAndSave(oldEntity); remove++; continue; } @@ -157,7 +158,7 @@ public class AchievementDbOperation { moveOld = true; moveNew = false; - RemoveEntity(oldEntity); + appDbContext.Achievements.RemoveAndSave(oldEntity); remove++; } else if (oldEntity.Id == newEntity.Id) @@ -172,8 +173,8 @@ public class AchievementDbOperation } else { - RemoveEntity(oldEntity); - AddEntity(newEntity); + appDbContext.Achievements.RemoveAndSave(oldEntity); + appDbContext.Achievements.AddAndSave(newEntity); update++; } } @@ -182,7 +183,7 @@ public class AchievementDbOperation // entity.Id > uiaf.Id moveOld = false; moveNew = true; - AddEntity(newEntity); + appDbContext.Achievements.AddAndSave(newEntity); add++; } } @@ -196,16 +197,4 @@ public class AchievementDbOperation return new(add, update, remove); } - - private void AddEntity(EntityAchievement entity) - { - appDbContext.Achievements.Add(entity); - appDbContext.SaveChanges(); - } - - private void RemoveEntity(EntityAchievement entity) - { - appDbContext.Achievements.Remove(entity); - appDbContext.SaveChanges(); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs index 2a21e0bc..62d9348b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs @@ -19,7 +19,7 @@ namespace Snap.Hutao.Service.Achievement; /// /// 成就服务 /// -[Injection(InjectAs.Transient, typeof(IAchievementService))] +[Injection(InjectAs.Scoped, typeof(IAchievementService))] internal class AchievementService : IAchievementService { private readonly AppDbContext appDbContext; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs index b95e49ea..87b24585 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs @@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.AvatarInfo; /// /// 角色信息服务 /// -[Injection(InjectAs.Transient, typeof(IAvatarInfoService))] +[Injection(InjectAs.Scoped, typeof(IAvatarInfoService))] internal class AvatarInfoService : IAvatarInfoService { private readonly AppDbContext appDbContext; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs index 61ee8144..8086beed 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs @@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog.Factory; /// /// 祈愿统计工厂 /// -[Injection(InjectAs.Transient, typeof(IGachaStatisticsFactory))] +[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))] internal class GachaStatisticsFactory : IGachaStatisticsFactory { private readonly IMetadataService metadataService; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index fef5135c..da78ae13 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -14,7 +14,6 @@ using Snap.Hutao.Model.Binding.Gacha.Abstraction; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.InterChange.GachaLog; using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.GachaLog.Factory; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; @@ -27,7 +26,7 @@ namespace Snap.Hutao.Service.GachaLog; /// /// 祈愿记录服务 /// -[Injection(InjectAs.Transient, typeof(IGachaLogService))] +[Injection(InjectAs.Scoped, typeof(IGachaLogService))] internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization { /// @@ -45,7 +44,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization private readonly IEnumerable urlProviders; private readonly GachaInfoClient gachaInfoClient; private readonly IMetadataService metadataService; - private readonly IInfoBarService infoBarService; private readonly IGachaStatisticsFactory gachaStatisticsFactory; private readonly ILogger logger; private readonly DbCurrent dbCurrent; @@ -66,7 +64,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization /// Url提供器集合 /// 祈愿记录客户端 /// 元数据服务 - /// 信息条服务 /// 祈愿统计工厂 /// 日志器 /// 消息器 @@ -75,7 +72,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization IEnumerable urlProviders, GachaInfoClient gachaInfoClient, IMetadataService metadataService, - IInfoBarService infoBarService, IGachaStatisticsFactory gachaStatisticsFactory, ILogger logger, IMessenger messenger) @@ -84,7 +80,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization this.urlProviders = urlProviders; this.gachaInfoClient = gachaInfoClient; this.metadataService = metadataService; - this.infoBarService = infoBarService; this.logger = logger; this.gachaStatisticsFactory = gachaStatisticsFactory; @@ -135,8 +130,8 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false); nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false); - idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); - idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); + idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false); + idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false); IsInitialized = true; } @@ -214,8 +209,8 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization archiveCollection.Remove(archive); // Sync database - appDbContext.GachaArchives.Remove(archive); - return appDbContext.SaveChangesAsync(); + appDbContext.GachaArchives.RemoveAndSave(archive); + return Task.CompletedTask; } private static Task RandomDelayAsync(CancellationToken token) @@ -232,7 +227,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization long trimId = appDbContext.GachaItems .Where(i => i.ArchiveId == archiveId) .OrderBy(i => i.Id) - .Take(1) .FirstOrDefault()?.Id ?? long.MaxValue; IEnumerable toAdd = list @@ -326,8 +320,7 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization if (archive == null) { GachaArchive created = GachaArchive.Create(uid); - appDbContext.GachaArchives.Add(created); - appDbContext.SaveChanges(); + appDbContext.GachaArchives.AddAndSave(created); archive = appDbContext.GachaArchives.Single(a => a.Uid == uid); GachaArchive temp = archive; @@ -346,10 +339,9 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization .Where(i => i.ArchiveId == archive.InnerId) .Where(i => i.QueryType == configType) .OrderByDescending(i => i.Id) - .Take(1) .FirstOrDefault(); - // MaxBy should be supported by .NET 7 + // TODO MaxBy should be supported by .NET 7 // .MaxBy(i => i.Id); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs index 8b29b3c3..4196cc51 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs @@ -4,8 +4,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Snap.Hutao.Context.Database; +using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Threading; -using Snap.Hutao.Extension; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Game.Locator; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 87b5511c..44931981 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -153,8 +153,8 @@ internal class UserService : IUserService // 检查 stoken 是否存在 if (cookie.ContainsSToken()) { - // insert stoken directly - userWithSameUid.Cookie.InsertSToken(uid, cookie); + // insert stoken + userWithSameUid.UpdateSToken(uid, cookie); appDbContext.Users.Update(userWithSameUid.Entity); appDbContext.SaveChanges(); diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs index 62598222..ffaa7611 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs @@ -30,7 +30,7 @@ namespace Snap.Hutao.ViewModel; /// /// 成就视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class AchievementViewModel : ObservableObject, ISupportCancellation, @@ -42,10 +42,10 @@ internal class AchievementViewModel new(nameof(Model.Binding.Achievement.IsChecked), SortDirection.Ascending); private readonly IMetadataService metadataService; - private readonly IAchievementService achievementService; private readonly IInfoBarService infoBarService; private readonly JsonSerializerOptions options; - private readonly IPickerFactory pickerFactory; + + private readonly IAchievementService achievementService; private readonly TaskCompletionSource openUICompletionSource = new(); @@ -67,7 +67,7 @@ internal class AchievementViewModel /// 信息条服务 /// Json序列化选项 /// 异步命令工厂 - /// 文件选择器工厂 + /// 范围工厂 /// 消息器 public AchievementViewModel( IMetadataService metadataService, @@ -75,14 +75,12 @@ internal class AchievementViewModel IInfoBarService infoBarService, JsonSerializerOptions options, IAsyncRelayCommandFactory asyncRelayCommandFactory, - IPickerFactory pickerFactory, IMessenger messenger) { this.metadataService = metadataService; this.achievementService = achievementService; this.infoBarService = infoBarService; this.options = options; - this.pickerFactory = pickerFactory; OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); ImportUIAFFromClipboardCommand = asyncRelayCommandFactory.Create(ImportUIAFFromClipboardAsync); @@ -416,6 +414,7 @@ internal class AchievementViewModel return; } + IPickerFactory pickerFactory = Ioc.Default.GetRequiredService(); FileOpenPicker picker = pickerFactory.GetFileOpenPicker(); picker.SuggestedStartLocation = PickerLocationId.Desktop; picker.CommitButtonText = "导入"; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs index fbc6d37f..758537d8 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs @@ -16,12 +16,10 @@ namespace Snap.Hutao.ViewModel; /// /// 公告视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class AnnouncementViewModel : ObservableObject, ISupportCancellation { private readonly IAnnouncementService announcementService; - private readonly INavigationService navigationService; - private readonly IInfoBarService infoBarService; private readonly ILogger logger; private AnnouncementWrapper? announcement; @@ -36,14 +34,10 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation /// 日志器 public AnnouncementViewModel( IAnnouncementService announcementService, - INavigationService navigationService, IAsyncRelayCommandFactory asyncRelayCommandFactory, - IInfoBarService infoBarService, ILogger logger) { this.announcementService = announcementService; - this.navigationService = navigationService; - this.infoBarService = infoBarService; this.logger = logger; OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); @@ -88,10 +82,12 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation { if (WebView2Helper.IsSupported) { + INavigationService navigationService = Ioc.Default.GetRequiredService(); navigationService.Navigate(data: new NavigationExtra(content)); } else { + IInfoBarService infoBarService = Ioc.Default.GetRequiredService(); infoBarService.Warning("尚未安装 WebView2 运行时。"); } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs index a991165a..d32215c1 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs @@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel; /// /// 角色属性视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation { private readonly IUserService userService; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs index 2fa5d654..86cfd206 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs @@ -16,7 +16,7 @@ namespace Snap.Hutao.ViewModel; /// /// 实验性功能视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class ExperimentalFeaturesViewModel : ObservableObject { private readonly IFileSystemLocation hutaoLocation; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs index 52b66a52..1c4b3e72 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs @@ -24,7 +24,7 @@ namespace Snap.Hutao.ViewModel; /// /// 祈愿记录视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class GachaLogViewModel : ObservableObject, ISupportCancellation { private readonly IGachaLogService gachaLogService; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoDatabaseViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoDatabaseViewModel.cs index 9056ff1f..8e5e220c 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoDatabaseViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoDatabaseViewModel.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao.ViewModel; /// /// 胡桃数据库视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation { private readonly IHtaoCache hutaoCache; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs index d7a8a4f1..b0945ec7 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs @@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Context.Database; -using Snap.Hutao.Extension; +using Snap.Hutao.Core.Database; using Snap.Hutao.Model.Entity; namespace Snap.Hutao.ViewModel; @@ -11,7 +11,7 @@ namespace Snap.Hutao.ViewModel; /// /// 测试视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class SettingViewModel : ObservableObject { private readonly AppDbContext appDbContext; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs index d44a266a..2f76298c 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs @@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel; /// /// 用户视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Singleton)] internal class UserViewModel : ObservableObject { private readonly IUserService userService; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs index 94230150..f6728b02 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs @@ -15,7 +15,7 @@ namespace Snap.Hutao.ViewModel; /// /// 角色资料视图模型 /// -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Scoped)] internal class WikiAvatarViewModel : ObservableObject { private readonly IMetadataService metadataService;