using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Gpulse.WCT.DataAnalyzer.Core.Infrastructure.LocalData; using Gpulse.WCT.DataAnalyzer.Core.Domain.Local; namespace Gpulse.WCT.DataAnalyzer.Core.Application; /// /// 维度表管理服务 - 负责维度表的CRUD操作(并发安全版本) /// public class DimensionService { private readonly WctMinerDbContext _context; private readonly ILogger _logger; public DimensionService(WctMinerDbContext context, ILogger logger) { _context = context; _logger = logger; } /// /// 获取或创建TX面板维度(并发安全) /// public async Task GetOrCreateTxPanelAsync(string name) { return await GetOrCreateDimensionAsync( () => _context.TxPanels.AsNoTracking().FirstOrDefaultAsync(p => p.Name == name), () => new TxPanel { Name = name }, entity => _context.TxPanels.Add(entity), name, "TX Panel" ); } /// /// 获取或创建TX硬件版本维度(并发安全) /// public async Task GetOrCreateTxHardwareAsync(string version) { return await GetOrCreateDimensionAsync( () => _context.TxHardwares.AsNoTracking().FirstOrDefaultAsync(h => h.Version == version), () => new TxHardware { Version = version }, entity => _context.TxHardwares.Add(entity), version, "TX Hardware" ); } /// /// 获取或创建TX软件版本维度(并发安全) /// public async Task GetOrCreateTxSoftwareAsync(string version) { return await GetOrCreateDimensionAsync( () => _context.TxSoftwares.AsNoTracking().FirstOrDefaultAsync(s => s.Version == version), () => new TxSoftware { Version = version }, entity => _context.TxSoftwares.Add(entity), version, "TX Software" ); } /// /// 获取或创建RX类型维度(并发安全) /// public async Task GetOrCreateRxTypeAsync(string name) { return await GetOrCreateDimensionAsync( () => _context.RxTypes.AsNoTracking().FirstOrDefaultAsync(r => r.Name == name), () => new RxType { Name = name }, entity => _context.RxTypes.Add(entity), name, "RX Type" ); } /// /// 通用的并发安全 GetOrCreate 模式 /// private async Task GetOrCreateDimensionAsync( Func> findExisting, Func createNew, Action addToContext, string identifier, string dimensionName) where T : class { const int maxRetries = 3; for (int attempt = 0; attempt < maxRetries; attempt++) { // 使用 AsNoTracking 避免 ChangeTracker 缓存 var existing = await findExisting(); if (existing != null) return existing; var newEntity = createNew(); addToContext(newEntity); try { await _context.SaveChangesAsync(); _logger.LogDebug("创建新 {DimensionName} 维度: {Identifier}", dimensionName, identifier); return newEntity; } catch (DbUpdateException ex) when (IsUniqueConstraintViolation(ex)) { // Detach 实体以避免 ChangeTracker 问题 _context.Entry(newEntity).State = EntityState.Detached; _logger.LogDebug("{DimensionName} 并发创建冲突,重试: {Identifier}", dimensionName, identifier); if (attempt < maxRetries - 1) { // 随机退避,减少并发冲突 await Task.Delay(Random.Shared.Next(5, 20)); } } } // 最后一次尝试:必然存在(唯一约束已生效) var finalEntity = await findExisting(); if (finalEntity == null) { // 极端情况:重试全部失败但数据不存在,抛出异常 throw new InvalidOperationException($"无法获取或创建 {dimensionName} 维度: {identifier}"); } return finalEntity; } /// /// 检测 SQLite 唯一约束违反错误 /// private static bool IsUniqueConstraintViolation(DbUpdateException ex) { return ex.InnerException is Microsoft.Data.Sqlite.SqliteException sqliteEx && sqliteEx.SqliteErrorCode == 19; // SQLITE_CONSTRAINT } }