dramaling-vocab-learning/backend/DramaLing.Api/Services/Infrastructure/Caching/DatabaseCacheManager.cs

106 lines
3.7 KiB
C#

using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
namespace DramaLing.Api.Services.Infrastructure.Caching;
public class DatabaseCacheManager : IDatabaseCacheManager
{
private readonly DramaLingDbContext _dbContext;
private readonly ICacheSerializer _serializer;
private readonly ILogger<DatabaseCacheManager> _logger;
public DatabaseCacheManager(
DramaLingDbContext dbContext,
ICacheSerializer serializer,
ILogger<DatabaseCacheManager> logger)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<T?> GetFromDatabaseCacheAsync<T>(string key) where T : class
{
try
{
if (!key.StartsWith("analysis:")) return null;
var hash = key.Replace("analysis:", "");
var cached = await _dbContext.SentenceAnalysisCache
.AsNoTracking()
.FirstOrDefaultAsync(c => c.InputTextHash == hash && c.ExpiresAt > DateTime.UtcNow);
if (cached != null)
{
// 更新訪問統計
cached.AccessCount++;
cached.LastAccessedAt = DateTime.UtcNow;
await _dbContext.SaveChangesAsync();
var jsonBytes = System.Text.Encoding.UTF8.GetBytes(cached.AnalysisResult);
var result = _serializer.Deserialize<T>(jsonBytes);
_logger.LogDebug("Database cache hit for key: {Key}", key);
return result;
}
_logger.LogDebug("Database cache miss for key: {Key}", key);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting from database cache for key: {Key}", key);
return null;
}
}
public async Task SaveToDatabaseCacheAsync<T>(string key, T value, TimeSpan expiry) where T : class
{
try
{
if (!key.StartsWith("analysis:")) return;
var hash = key.Replace("analysis:", "");
var expiresAt = DateTime.UtcNow.Add(expiry);
var existing = await _dbContext.SentenceAnalysisCache
.FirstOrDefaultAsync(c => c.InputTextHash == hash);
var jsonBytes = _serializer.Serialize(value);
var jsonString = System.Text.Encoding.UTF8.GetString(jsonBytes);
if (existing != null)
{
existing.AnalysisResult = jsonString;
existing.ExpiresAt = expiresAt;
existing.AccessCount++;
existing.LastAccessedAt = DateTime.UtcNow;
}
else
{
var cacheItem = new SentenceAnalysisCache
{
Id = Guid.NewGuid(),
InputTextHash = hash,
InputText = "",
AnalysisResult = jsonString,
CreatedAt = DateTime.UtcNow,
ExpiresAt = expiresAt,
AccessCount = 1,
LastAccessedAt = DateTime.UtcNow
};
_dbContext.SentenceAnalysisCache.Add(cacheItem);
}
await _dbContext.SaveChangesAsync();
_logger.LogDebug("Database cache saved for key: {Key}", key);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving to database cache for key: {Key}", key);
}
}
}