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