diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs index 199f9d11..fc3baa03 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs @@ -33,17 +33,17 @@ internal static class IocConfiguration return services .AddTransient(typeof(Database.ScopedDbCurrent<,>)) .AddTransient(typeof(Database.ScopedDbCurrent<,,>)) - .AddDbContext(AddDbContextCore); + .AddDbContextPool(AddDbContextCore); } - private static void AddDbContextCore(IServiceProvider provider, DbContextOptionsBuilder builder) + private static void AddDbContextCore(IServiceProvider serviceProvider, DbContextOptionsBuilder builder) { - RuntimeOptions runtimeOptions = provider.GetRequiredService(); + RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService(); string dbFile = System.IO.Path.Combine(runtimeOptions.DataFolder, "Userdata.db"); string sqlConnectionString = $"Data Source={dbFile}"; // Temporarily create a context - using (AppDbContext context = AppDbContext.Create(sqlConnectionString)) + using (AppDbContext context = AppDbContext.Create(serviceProvider, sqlConnectionString)) { if (context.Database.GetPendingMigrations().Any()) { diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index ee44ddcd..d03ac2d8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Snap.Hutao.Core.Logging; using Snap.Hutao.Model.Entity.Configuration; using System.Diagnostics; @@ -24,18 +25,8 @@ internal sealed class AppDbContext : DbContext public AppDbContext(DbContextOptions options) : base(options) { - } - - /// - /// 构造一个新的应用程序数据库上下文 - /// - /// 选项 - /// 日志器 - public AppDbContext(DbContextOptions options, ILogger logger) - : this(options) - { - this.logger = logger; - logger.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); + logger = this.GetService>(); + logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); } public DbSet Settings { get; set; } = default!; @@ -74,14 +65,14 @@ internal sealed class AppDbContext : DbContext public DbSet SpiralAbysses { get; set; } = default!; - /// - /// 构造一个临时的应用程序数据库上下文 - /// - /// 连接字符串 - /// 应用程序数据库上下文 - public static AppDbContext Create(string sqlConnectionString) + public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString) { - return new(new DbContextOptionsBuilder().UseSqlite(sqlConnectionString).Options); + DbContextOptions options = new DbContextOptionsBuilder() + .UseApplicationServiceProvider(serviceProvider) + .UseSqlite(sqlConnectionString) + .Options; + + return new(options); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContextDesignTimeFactory.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContextDesignTimeFactory.cs index 1b37217e..eec1ea44 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContextDesignTimeFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContextDesignTimeFactory.cs @@ -18,7 +18,7 @@ internal sealed class AppDbContextDesignTimeFactory : IDesignTimeDbContextFactor #if DEBUG // TODO: replace with your own database file path. string userdataDbName = @"D:\Hutao\Userdata.db"; - return AppDbContext.Create($"Data Source={userdataDbName}"); + return AppDbContext.Create(default!, $"Data Source={userdataDbName}"); #else throw Must.NeverHappen(); #endif diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs index 40963af4..0042f693 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs @@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.Hutao; internal sealed partial class HutaoAsAService : IHutaoAsAService { private const int AnnouncementDuration = 30; - private readonly HutaoAsAServiceClient hutaoAsServiceClient; + private readonly IServiceScopeFactory serviceScopeFactory; private ObservableCollection? announcements; @@ -29,7 +29,13 @@ internal sealed partial class HutaoAsAService : IHutaoAsAService ApplicationDataCompositeValue excludedIds = LocalSetting.Get(SettingKeys.ExcludedAnnouncementIds, new ApplicationDataCompositeValue()); List data = excludedIds.Select(kvp => long.Parse(kvp.Key, CultureInfo.InvariantCulture)).ToList(); - Response> response = await hutaoAsServiceClient.GetAnnouncementListAsync(data, token).ConfigureAwait(false); + + Response> response; + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + HutaoAsAServiceClient hutaoAsAServiceClient = scope.ServiceProvider.GetRequiredService(); + response = await hutaoAsAServiceClient.GetAnnouncementListAsync(data, token).ConfigureAwait(false); + } if (response.IsOk()) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs index 34527a60..257ceb00 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs @@ -15,7 +15,7 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe { private readonly TaskCompletionSource initializeCompletionSource = new(); - private readonly HutaoPassportClient passportClient; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly ITaskContext taskContext; private readonly HutaoUserOptions options; @@ -40,19 +40,24 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe } else { - Web.Response.Response response = await passportClient.LoginAsync(userName, password, token).ConfigureAwait(false); + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); - if (response.IsOk()) - { - if (await options.PostLoginSucceedAsync(passportClient, taskContext, userName, password, response.Data).ConfigureAwait(false)) + Web.Response.Response response = await hutaoPassportClient.LoginAsync(userName, password, token).ConfigureAwait(false); + + if (response.IsOk()) { - isInitialized = true; + if (await options.PostLoginSucceedAsync(hutaoPassportClient, taskContext, userName, password, response.Data).ConfigureAwait(false)) + { + isInitialized = true; + } + } + else + { + await taskContext.SwitchToMainThreadAsync(); + options.PostLoginFailed(); } - } - else - { - await taskContext.SwitchToMainThreadAsync(); - options.PostLoginFailed(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs index 8c46b813..43f6e26c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs @@ -29,12 +29,12 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi private readonly TaskCompletionSource initializeCompletionSource = new(); + private readonly IHttpClientFactory httpClientFactory; private readonly ILogger logger; private readonly MetadataOptions metadataOptions; private readonly IInfoBarService infoBarService; private readonly JsonSerializerOptions options; private readonly IMemoryCache memoryCache; - private readonly HttpClient httpClient; private bool isInitialized; @@ -85,7 +85,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi else { FileNotFoundException exception = new(SH.ServiceMetadataFileNotFound, fileName); - throw ThrowHelper.UserdataCorrupted(SH.ServiceMetadataFileNotFound, exception); + throw HutaoException.Throw(SH.ServiceMetadataFileNotFound, exception); } } @@ -119,10 +119,13 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi Dictionary? metadataFileHashs; try { - // download meta check file - metadataFileHashs = await httpClient - .GetFromJsonAsync>(metadataOptions.GetLocalizedRemoteFile(MetaFileName), options, token) - .ConfigureAwait(false); + // Download meta check file + using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService))) + { + metadataFileHashs = await httpClient + .GetFromJsonAsync>(metadataOptions.GetLocalizedRemoteFile(MetaFileName), options, token) + .ConfigureAwait(false); + } if (metadataFileHashs is null) { @@ -176,9 +179,13 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi private async ValueTask DownloadMetadataSourceFilesAsync(string fileFullName, CancellationToken token) { - Stream sourceStream = await httpClient - .GetStreamAsync(metadataOptions.GetLocalizedRemoteFile(fileFullName), token) - .ConfigureAwait(false); + Stream sourceStream; + using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService))) + { + sourceStream = await httpClient + .GetStreamAsync(metadataOptions.GetLocalizedRemoteFile(fileFullName), token) + .ConfigureAwait(false); + } // Write stream while convert LF to CRLF using (StreamReader streamReader = new(sourceStream)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs index 4833570b..3676e8e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao.Service.User; [Injection(InjectAs.Singleton, typeof(IUserFingerprintService))] internal sealed partial class UserFingerprintService : IUserFingerprintService { - private readonly DeviceFpClient deviceFpClient; + private readonly IServiceScopeFactory serviceScopeFactory; public async ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default) { @@ -101,7 +101,13 @@ internal sealed partial class UserFingerprintService : IUserFingerprintService DeviceFp = string.IsNullOrEmpty(user.Fingerprint) ? Core.Random.GetLowerHexString(13) : user.Fingerprint, }; - Response response = await deviceFpClient.GetFingerprintAsync(data, token).ConfigureAwait(false); + Response response; + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + DeviceFpClient deviceFpClient = scope.ServiceProvider.GetRequiredService(); + response = await deviceFpClient.GetFingerprintAsync(data, token).ConfigureAwait(false); + } + user.TryUpdateFingerprint(response.IsOk() ? response.Data.DeviceFp : string.Empty); user.NeedDbUpdateAfterResume = true; diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportRegisterDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportRegisterDialog.xaml.cs index 307c4353..f355204d 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportRegisterDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportRegisterDialog.xaml.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.View.Dialog; [DependencyProperty("VerifyCode", typeof(string))] internal sealed partial class HutaoPassportRegisterDialog : ContentDialog { - private readonly HutaoPassportClient homaPassportClient; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -23,7 +23,7 @@ internal sealed partial class HutaoPassportRegisterDialog : ContentDialog InitializeComponent(); taskContext = serviceProvider.GetRequiredService(); - homaPassportClient = serviceProvider.GetRequiredService(); + serviceScopeFactory = serviceProvider.GetRequiredService(); infoBarService = serviceProvider.GetRequiredService(); } @@ -49,7 +49,12 @@ internal sealed partial class HutaoPassportRegisterDialog : ContentDialog return; } - HutaoResponse response = await homaPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.Registration).ConfigureAwait(false); - infoBarService.Information(response.GetLocalizationMessage()); + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.Registration).ConfigureAwait(false); + infoBarService.Information(response.GetLocalizationMessage()); + } } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportResetPasswordDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportResetPasswordDialog.xaml.cs index 0224ce4d..38550210 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportResetPasswordDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportResetPasswordDialog.xaml.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.View.Dialog; [DependencyProperty("VerifyCode", typeof(string))] internal sealed partial class HutaoPassportResetPasswordDialog : ContentDialog { - private readonly HutaoPassportClient homaPassportClient; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -23,7 +23,7 @@ internal sealed partial class HutaoPassportResetPasswordDialog : ContentDialog InitializeComponent(); taskContext = serviceProvider.GetRequiredService(); - homaPassportClient = serviceProvider.GetRequiredService(); + serviceScopeFactory = serviceProvider.GetRequiredService(); infoBarService = serviceProvider.GetRequiredService(); } @@ -49,7 +49,12 @@ internal sealed partial class HutaoPassportResetPasswordDialog : ContentDialog return; } - HutaoResponse response = await homaPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.ResetPassword).ConfigureAwait(false); - infoBarService.Information(response.GetLocalizationMessage()); + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.ResetPassword).ConfigureAwait(false); + infoBarService.Information(response.GetLocalizationMessage()); + } } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportUnregisterDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportUnregisterDialog.xaml.cs index 993196a1..9584b57e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportUnregisterDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/HutaoPassportUnregisterDialog.xaml.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.View.Dialog; [DependencyProperty("VerifyCode", typeof(string))] internal sealed partial class HutaoPassportUnregisterDialog : ContentDialog { - private readonly HutaoPassportClient homaPassportClient; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -23,7 +23,7 @@ internal sealed partial class HutaoPassportUnregisterDialog : ContentDialog InitializeComponent(); taskContext = serviceProvider.GetRequiredService(); - homaPassportClient = serviceProvider.GetRequiredService(); + serviceScopeFactory = serviceProvider.GetRequiredService(); infoBarService = serviceProvider.GetRequiredService(); } @@ -49,7 +49,12 @@ internal sealed partial class HutaoPassportUnregisterDialog : ContentDialog return; } - HutaoResponse response = await homaPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.CancelRegistration).ConfigureAwait(false); - infoBarService.Information(response.GetLocalizationMessage()); + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.CancelRegistration).ConfigureAwait(false); + infoBarService.Information(response.GetLocalizationMessage()); + } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs index 8dcf373a..7bcd995e 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs @@ -20,7 +20,7 @@ namespace Snap.Hutao.ViewModel.Setting; internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel { private readonly IContentDialogFactory contentDialogFactory; - private readonly HutaoPassportClient homaPassportClient; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly HutaoUserOptions hutaoUserOptions; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -46,12 +46,17 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel return; } - HutaoResponse response = await homaPassportClient.RegisterAsync(username, password, verifyCode).ConfigureAwait(false); - - if (response.IsOk()) + using (IServiceScope scope = serviceScopeFactory.CreateScope()) { - infoBarService.Information(response.GetLocalizationMessageOrMessage()); - await hutaoUserOptions.PostLoginSucceedAsync(homaPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.RegisterAsync(username, password, verifyCode).ConfigureAwait(false); + + if (response.IsOk()) + { + infoBarService.Information(response.GetLocalizationMessageOrMessage()); + await hutaoUserOptions.PostLoginSucceedAsync(hutaoPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + } } } } @@ -71,14 +76,19 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel return; } - HutaoResponse response = await homaPassportClient.UnregisterAsync(username, password, verifyCode).ConfigureAwait(false); - - if (response.IsOk()) + using (IServiceScope scope = serviceScopeFactory.CreateScope()) { - infoBarService.Information(response.GetLocalizationMessageOrMessage()); + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); - await taskContext.SwitchToMainThreadAsync(); - hutaoUserOptions.PostLogoutOrUnregister(); + HutaoResponse response = await hutaoPassportClient.UnregisterAsync(username, password, verifyCode).ConfigureAwait(false); + + if (response.IsOk()) + { + infoBarService.Information(response.GetLocalizationMessageOrMessage()); + + await taskContext.SwitchToMainThreadAsync(); + hutaoUserOptions.PostLogoutOrUnregister(); + } } } } @@ -98,12 +108,17 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel return; } - HutaoResponse response = await homaPassportClient.LoginAsync(username, password).ConfigureAwait(false); - - if (response.IsOk()) + using (IServiceScope scope = serviceScopeFactory.CreateScope()) { - infoBarService.Information(response.GetLocalizationMessageOrMessage()); - await hutaoUserOptions.PostLoginSucceedAsync(homaPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.LoginAsync(username, password).ConfigureAwait(false); + + if (response.IsOk()) + { + infoBarService.Information(response.GetLocalizationMessageOrMessage()); + await hutaoUserOptions.PostLoginSucceedAsync(hutaoPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + } } } } @@ -129,12 +144,17 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel return; } - HutaoResponse response = await homaPassportClient.ResetPasswordAsync(username, password, verifyCode).ConfigureAwait(false); - - if (response.IsOk()) + using (IServiceScope scope = serviceScopeFactory.CreateScope()) { - infoBarService.Information(response.GetLocalizationMessageOrMessage()); - await hutaoUserOptions.PostLoginSucceedAsync(homaPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService(); + + HutaoResponse response = await hutaoPassportClient.ResetPasswordAsync(username, password, verifyCode).ConfigureAwait(false); + + if (response.IsOk()) + { + infoBarService.Information(response.GetLocalizationMessageOrMessage()); + await hutaoUserOptions.PostLoginSucceedAsync(hutaoPassportClient, taskContext, username, password, response.Data).ConfigureAwait(false); + } } } }