fix view model scope

This commit is contained in:
DismissedLight
2022-10-25 13:12:50 +08:00
parent fa19f7e817
commit 792a701183
33 changed files with 303 additions and 169 deletions

View File

@@ -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";
/// <inheritdoc/>
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)

View File

@@ -28,9 +28,6 @@ public class ScopedPage : Page
serviceScope = Ioc.Default.CreateScope();
}
/// <inheritdoc cref="IServiceScope.ServiceProvider"/>
public IServiceProvider ServiceProvider { get => serviceScope.ServiceProvider; }
/// <summary>
/// 初始化
/// </summary>
@@ -38,7 +35,7 @@ public class ScopedPage : Page
public void InitializeWith<TViewModel>()
where TViewModel : class, ISupportCancellation
{
ISupportCancellation viewModel = ServiceProvider.GetRequiredService<TViewModel>();
ISupportCancellation viewModel = serviceScope.ServiceProvider.GetRequiredService<TViewModel>();
viewModel.CancellationToken = viewLoadingCancellationTokenSource.Token;
DataContext = viewModel;
}

View File

@@ -23,6 +23,8 @@ public abstract class CacheBase<T>
{
private readonly SemaphoreSlim cacheFolderSemaphore = new(1);
private readonly ILogger logger;
// violate di rule
private readonly HttpClient httpClient;
private StorageFolder? baseFolder;

View File

@@ -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<TEntity, TMessage>
where TEntity : class, ISelectable
where TMessage : Message.ValueChangedMessage<TEntity>, new()
{
private readonly DbContext dbContext;
private readonly DbSet<TEntity> dbSet;
private readonly IMessenger messenger;
@@ -31,7 +29,6 @@ internal class DbCurrent<TEntity, TMessage>
///
public DbCurrent(DbSet<TEntity> dbSet, IMessenger messenger)
{
this.dbContext = dbSet.Context();
this.dbSet = dbSet;
this.messenger = messenger;
}

View File

@@ -4,7 +4,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Snap.Hutao.Extension;
namespace Snap.Hutao.Core.Database;
/// <summary>
/// 数据库集合上下文
@@ -50,6 +50,34 @@ public static class DbSetExtension
return entry;
}
/// <summary>
/// 添加并保存
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <param name="dbSet">数据库集</param>
/// <param name="entity">实体</param>
/// <returns>影响条数</returns>
public static int AddAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
where TEntity : class
{
dbSet.Add(entity);
return dbSet.Context().SaveChanges();
}
/// <summary>
/// 移除并保存
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <param name="dbSet">数据库集</param>
/// <param name="entity">实体</param>
/// <returns>影响条数</returns>
public static int RemoveAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
where TEntity : class
{
dbSet.Remove(entity);
return dbSet.Context().SaveChanges();
}
/// <summary>
/// 更新并保存
/// </summary>

View File

@@ -17,4 +17,9 @@ public enum InjectAs
/// 指示应注册为短期对象
/// </summary>
Transient,
/// <summary>
/// 指示应注册为范围对象
/// </summary>
Scoped,
}

View File

@@ -21,4 +21,4 @@ internal class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerabl
{
writer.WriteStringValue(string.Join(',', value));
}
}
}

View File

@@ -8,11 +8,6 @@ namespace Snap.Hutao.Core.Setting;
/// </summary>
internal static class SettingKeys
{
/// <summary>
/// 上次打开时App的版本
/// </summary>
public const string LastAppVersion = "LastAppVersion";
/// <summary>
/// 窗体左侧
/// </summary>

View File

@@ -38,18 +38,6 @@ public static class Must
}
}
/// <summary>
/// 任务异常
/// </summary>
/// <param name="message">异常消息</param>
/// <returns>异常的任务</returns>
[SuppressMessage("", "VSTHRD200")]
public static Task Fault(string message)
{
InvalidOperationException exception = new(message);
return Task.FromException(exception);
}
/// <summary>
/// 任务异常
/// </summary>

View File

@@ -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;
/// <summary>
/// 用于视图绑定的成就
/// </summary>
public class Achievement : Observable
public class Achievement : ObservableObject
{
/// <summary>
/// 满进度占位符
@@ -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
/// </summary>
public string Time
{
get => entity.Time.ToString("yyyy-MM-dd HH:mm:ss");
get => entity.Time.ToString("yyyy.MM.dd HH:mm:ss");
}
}

View File

@@ -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;
/// <summary>
/// 用于视图绑定的用户
/// </summary>
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);
}
/// <inheritdoc cref="EntityUser.IsSelected"/>
@@ -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));
}
}
/// <summary>
/// 是否拥有 SToken
/// </summary>
public bool HasSToken
{
get => inner.Cookie.ContainsSToken();
}
/// <summary>
@@ -71,6 +84,17 @@ public class User : Observable
/// </summary>
public bool IsInitialized { get => isInitialized; }
/// <summary>
/// 更新SToken
/// </summary>
/// <param name="uid">uid</param>
/// <param name="cookie">cookie</param>
internal void UpdateSToken(string uid, Cookie cookie)
{
Cookie.InsertSToken(uid, cookie);
OnPropertyChanged(nameof(HasSToken));
}
/// <summary>
/// 从数据库恢复用户
/// </summary>
@@ -79,11 +103,7 @@ public class User : Observable
/// <param name="userGameRoleClient">角色客户端</param>
/// <param name="token">取消令牌</param>
/// <returns>用户是否初始化完成若Cookie失效会返回 <see langword="false"/> </returns>
internal static async Task<User?> ResumeAsync(
EntityUser inner,
UserClient userClient,
BindingClient userGameRoleClient,
CancellationToken token = default)
internal static async Task<User?> 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
/// <param name="userGameRoleClient">角色客户端</param>
/// <param name="token">取消令牌</param>
/// <returns>用户是否初始化完成若Cookie失效会返回 <see langword="null"/> </returns>
internal static async Task<User?> CreateAsync(
Cookie cookie,
UserClient userClient,
BindingClient userGameRoleClient,
CancellationToken token = default)
internal static async Task<User?> 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<bool> InitializeCoreAsync(
UserClient userClient,
BindingClient userGameRoleClient,
CancellationToken token = default)
private async Task<bool> 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();
}
}

View File

@@ -1,44 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Model;
/// <summary>
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
/// </summary>
public abstract class Observable : INotifyPropertyChanged
{
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// 设置字段的值
/// </summary>
/// <typeparam name="T">字段类型</typeparam>
/// <param name="storage">现有值</param>
/// <param name="value">新的值</param>
/// <param name="propertyName">属性名称</param>
/// <returns>项是否更新</returns>
protected bool Set<T>([NotNullIfNotNull("value")] ref T storage, T value, [CallerMemberName] string propertyName = default!)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// 触发 <see cref="PropertyChanged"/>
/// </summary>
/// <param name="propertyName">属性名称</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@@ -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;
/// <summary>
/// 角色Id
/// </summary>
[JsonConverter(typeof(AvatarIdConverter))]
public readonly struct AvatarId : IEquatable<AvatarId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="AvatarId"/> struct.
/// </summary>
/// <param name="value">value</param>
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);
}
/// <inheritdoc/>
public bool Equals(AvatarId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is AvatarId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Primitive.Converter;
/// <summary>
/// 角色Id转换器
/// </summary>
internal class AvatarIdConverter : JsonConverter<AvatarId>
{
/// <inheritdoc/>
public override AvatarId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetInt32();
}
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, AvatarId value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value);
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Primitive.Converter;
/// <summary>
/// 武器Id转换器
/// </summary>
internal class WeaponIdConverter : JsonConverter<WeaponId>
{
/// <inheritdoc/>
public override WeaponId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetInt32();
}
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, WeaponId value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value);
}
}

View File

@@ -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;
/// <summary>
/// 武器Id
/// </summary>
[JsonConverter(typeof(WeaponIdConverter))]
public readonly struct WeaponId : IEquatable<WeaponId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="WeaponId"/> struct.
/// </summary>
/// <param name="value">value</param>
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);
}
/// <inheritdoc/>
public bool Equals(WeaponId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is WeaponId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
namespace Snap.Hutao.Model;
/// <summary>
@@ -8,7 +10,7 @@ namespace Snap.Hutao.Model;
/// 默认为选中状态
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
public class Selectable<T> : Observable
public class Selectable<T> : ObservableObject
where T : class
{
private readonly Action? selectedChanged;
@@ -35,7 +37,7 @@ public class Selectable<T> : Observable
get => isSelected;
set
{
Set(ref isSelected, value);
SetProperty(ref isSelected, value);
selectedChanged?.Invoke();
}
}
@@ -43,5 +45,5 @@ public class Selectable<T> : Observable
/// <summary>
/// 存放的对象
/// </summary>
public T Value { get => value; set => Set(ref this.value, value); }
public T Value { get => value; set => SetProperty(ref this.value, value); }
}

View File

@@ -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
/// <returns>导入结果</returns>
public ImportResult Overwrite(Guid archiveId, IEnumerable<EntityAchievement> items)
{
IQueryable<EntityAchievement> oldData = appDbContext.Achievements
IOrderedQueryable<EntityAchievement> 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();
}
}

View File

@@ -19,7 +19,7 @@ namespace Snap.Hutao.Service.Achievement;
/// <summary>
/// 成就服务
/// </summary>
[Injection(InjectAs.Transient, typeof(IAchievementService))]
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
internal class AchievementService : IAchievementService
{
private readonly AppDbContext appDbContext;

View File

@@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.AvatarInfo;
/// <summary>
/// 角色信息服务
/// </summary>
[Injection(InjectAs.Transient, typeof(IAvatarInfoService))]
[Injection(InjectAs.Scoped, typeof(IAvatarInfoService))]
internal class AvatarInfoService : IAvatarInfoService
{
private readonly AppDbContext appDbContext;

View File

@@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
/// <summary>
/// 祈愿统计工厂
/// </summary>
[Injection(InjectAs.Transient, typeof(IGachaStatisticsFactory))]
[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))]
internal class GachaStatisticsFactory : IGachaStatisticsFactory
{
private readonly IMetadataService metadataService;

View File

@@ -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;
/// <summary>
/// 祈愿记录服务
/// </summary>
[Injection(InjectAs.Transient, typeof(IGachaLogService))]
[Injection(InjectAs.Scoped, typeof(IGachaLogService))]
internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
{
/// <summary>
@@ -45,7 +44,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
private readonly IEnumerable<IGachaLogUrlProvider> urlProviders;
private readonly GachaInfoClient gachaInfoClient;
private readonly IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
private readonly ILogger<GachaLogService> logger;
private readonly DbCurrent<GachaArchive, Message.GachaArchiveChangedMessage> dbCurrent;
@@ -66,7 +64,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
/// <param name="urlProviders">Url提供器集合</param>
/// <param name="gachaInfoClient">祈愿记录客户端</param>
/// <param name="metadataService">元数据服务</param>
/// <param name="infoBarService">信息条服务</param>
/// <param name="gachaStatisticsFactory">祈愿统计工厂</param>
/// <param name="logger">日志器</param>
/// <param name="messenger">消息器</param>
@@ -75,7 +72,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
IEnumerable<IGachaLogUrlProvider> urlProviders,
GachaInfoClient gachaInfoClient,
IMetadataService metadataService,
IInfoBarService infoBarService,
IGachaStatisticsFactory gachaStatisticsFactory,
ILogger<GachaLogService> 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<GachaItem> 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);
}

View File

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

View File

@@ -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();

View File

@@ -30,7 +30,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 成就视图模型
/// </summary>
[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<bool> openUICompletionSource = new();
@@ -67,7 +67,7 @@ internal class AchievementViewModel
/// <param name="infoBarService">信息条服务</param>
/// <param name="options">Json序列化选项</param>
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
/// <param name="pickerFactory">文件选择器工厂</param>
/// <param name="scopeFactory">范围工厂</param>
/// <param name="messenger">消息器</param>
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<IPickerFactory>();
FileOpenPicker picker = pickerFactory.GetFileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.Desktop;
picker.CommitButtonText = "导入";

View File

@@ -16,12 +16,10 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 公告视图模型
/// </summary>
[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<AnnouncementViewModel> logger;
private AnnouncementWrapper? announcement;
@@ -36,14 +34,10 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
/// <param name="logger">日志器</param>
public AnnouncementViewModel(
IAnnouncementService announcementService,
INavigationService navigationService,
IAsyncRelayCommandFactory asyncRelayCommandFactory,
IInfoBarService infoBarService,
ILogger<AnnouncementViewModel> 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<INavigationService>();
navigationService.Navigate<AnnouncementContentPage>(data: new NavigationExtra(content));
}
else
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
infoBarService.Warning("尚未安装 WebView2 运行时。");
}
}

View File

@@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 角色属性视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
{
private readonly IUserService userService;

View File

@@ -16,7 +16,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 实验性功能视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class ExperimentalFeaturesViewModel : ObservableObject
{
private readonly IFileSystemLocation hutaoLocation;

View File

@@ -24,7 +24,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 祈愿记录视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class GachaLogViewModel : ObservableObject, ISupportCancellation
{
private readonly IGachaLogService gachaLogService;

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 胡桃数据库视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation
{
private readonly IHtaoCache hutaoCache;

View File

@@ -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;
/// <summary>
/// 测试视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class SettingViewModel : ObservableObject
{
private readonly AppDbContext appDbContext;

View File

@@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 用户视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Singleton)]
internal class UserViewModel : ObservableObject
{
private readonly IUserService userService;

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.ViewModel;
/// <summary>
/// 角色资料视图模型
/// </summary>
[Injection(InjectAs.Transient)]
[Injection(InjectAs.Scoped)]
internal class WikiAvatarViewModel : ObservableObject
{
private readonly IMetadataService metadataService;