adjust HttpClient scope

This commit is contained in:
Lightczx
2024-04-22 16:51:35 +08:00
parent ca64c3e0ef
commit 9ecb3d5821
11 changed files with 132 additions and 82 deletions

View File

@@ -33,17 +33,17 @@ internal static class IocConfiguration
return services
.AddTransient(typeof(Database.ScopedDbCurrent<,>))
.AddTransient(typeof(Database.ScopedDbCurrent<,,>))
.AddDbContext<AppDbContext>(AddDbContextCore);
.AddDbContextPool<AppDbContext>(AddDbContextCore);
}
private static void AddDbContextCore(IServiceProvider provider, DbContextOptionsBuilder builder)
private static void AddDbContextCore(IServiceProvider serviceProvider, DbContextOptionsBuilder builder)
{
RuntimeOptions runtimeOptions = provider.GetRequiredService<RuntimeOptions>();
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
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())
{

View File

@@ -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<AppDbContext> options)
: base(options)
{
}
/// <summary>
/// 构造一个新的应用程序数据库上下文
/// </summary>
/// <param name="options">选项</param>
/// <param name="logger">日志器</param>
public AppDbContext(DbContextOptions<AppDbContext> options, ILogger<AppDbContext> logger)
: this(options)
{
this.logger = logger;
logger.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green));
logger = this.GetService<ILogger<AppDbContext>>();
logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green));
}
public DbSet<SettingEntry> Settings { get; set; } = default!;
@@ -74,14 +65,14 @@ internal sealed class AppDbContext : DbContext
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
/// <summary>
/// 构造一个临时的应用程序数据库上下文
/// </summary>
/// <param name="sqlConnectionString">连接字符串</param>
/// <returns>应用程序数据库上下文</returns>
public static AppDbContext Create(string sqlConnectionString)
public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString)
{
return new(new DbContextOptionsBuilder<AppDbContext>().UseSqlite(sqlConnectionString).Options);
DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
.UseApplicationServiceProvider(serviceProvider)
.UseSqlite(sqlConnectionString)
.Options;
return new(options);
}
/// <inheritdoc/>

View File

@@ -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

View File

@@ -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<HutaoAnnouncement>? announcements;
@@ -29,7 +29,13 @@ internal sealed partial class HutaoAsAService : IHutaoAsAService
ApplicationDataCompositeValue excludedIds = LocalSetting.Get(SettingKeys.ExcludedAnnouncementIds, new ApplicationDataCompositeValue());
List<long> data = excludedIds.Select(kvp => long.Parse(kvp.Key, CultureInfo.InvariantCulture)).ToList();
Response<List<HutaoAnnouncement>> response = await hutaoAsServiceClient.GetAnnouncementListAsync(data, token).ConfigureAwait(false);
Response<List<HutaoAnnouncement>> response;
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
HutaoAsAServiceClient hutaoAsAServiceClient = scope.ServiceProvider.GetRequiredService<HutaoAsAServiceClient>();
response = await hutaoAsAServiceClient.GetAnnouncementListAsync(data, token).ConfigureAwait(false);
}
if (response.IsOk())
{

View File

@@ -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<string> response = await passportClient.LoginAsync(userName, password, token).ConfigureAwait(false);
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
HutaoPassportClient hutaoPassportClient = scope.ServiceProvider.GetRequiredService<HutaoPassportClient>();
if (response.IsOk())
{
if (await options.PostLoginSucceedAsync(passportClient, taskContext, userName, password, response.Data).ConfigureAwait(false))
Web.Response.Response<string> 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();
}
}

View File

@@ -29,12 +29,12 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
private readonly TaskCompletionSource initializeCompletionSource = new();
private readonly IHttpClientFactory httpClientFactory;
private readonly ILogger<MetadataService> 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<string, string>? metadataFileHashs;
try
{
// download meta check file
metadataFileHashs = await httpClient
.GetFromJsonAsync<Dictionary<string, string>>(metadataOptions.GetLocalizedRemoteFile(MetaFileName), options, token)
.ConfigureAwait(false);
// Download meta check file
using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(MetadataService)))
{
metadataFileHashs = await httpClient
.GetFromJsonAsync<Dictionary<string, string>>(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))

View File

@@ -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<DeviceFpWrapper> response = await deviceFpClient.GetFingerprintAsync(data, token).ConfigureAwait(false);
Response<DeviceFpWrapper> response;
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
DeviceFpClient deviceFpClient = scope.ServiceProvider.GetRequiredService<DeviceFpClient>();
response = await deviceFpClient.GetFingerprintAsync(data, token).ConfigureAwait(false);
}
user.TryUpdateFingerprint(response.IsOk() ? response.Data.DeviceFp : string.Empty);
user.NeedDbUpdateAfterResume = true;

View File

@@ -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<ITaskContext>();
homaPassportClient = serviceProvider.GetRequiredService<HutaoPassportClient>();
serviceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
}
@@ -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<HutaoPassportClient>();
HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.Registration).ConfigureAwait(false);
infoBarService.Information(response.GetLocalizationMessage());
}
}
}

View File

@@ -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<ITaskContext>();
homaPassportClient = serviceProvider.GetRequiredService<HutaoPassportClient>();
serviceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
}
@@ -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<HutaoPassportClient>();
HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.ResetPassword).ConfigureAwait(false);
infoBarService.Information(response.GetLocalizationMessage());
}
}
}

View File

@@ -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<ITaskContext>();
homaPassportClient = serviceProvider.GetRequiredService<HutaoPassportClient>();
serviceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
}
@@ -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<HutaoPassportClient>();
HutaoResponse response = await hutaoPassportClient.RequestVerifyAsync(UserName, VerifyCodeRequestType.CancelRegistration).ConfigureAwait(false);
infoBarService.Information(response.GetLocalizationMessage());
}
}
}

View File

@@ -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<string> 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<HutaoPassportClient>();
HutaoResponse<string> 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<HutaoPassportClient>();
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<string> 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<HutaoPassportClient>();
HutaoResponse<string> 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<string> 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<HutaoPassportClient>();
HutaoResponse<string> 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);
}
}
}
}