feat(data): rewrite all repositories to use EF Core ClaudeDoDbContext
Replace raw ADO.NET implementations with EF Core LINQ queries and ExecuteUpdate/ExecuteDelete for bulk operations. TaskRepository preserves FlipAllRunningToFailedAsync(reason) signature and keeps raw SQL for the atomic queue claim (UPDATE...RETURNING). GetByListAsync alias kept for backwards compat. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,139 +1,44 @@
|
||||
using System.Globalization;
|
||||
using ClaudeDo.Data.Models;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClaudeDo.Data.Repositories;
|
||||
|
||||
public sealed class TaskRunRepository
|
||||
{
|
||||
private readonly SqliteConnectionFactory _factory;
|
||||
private readonly ClaudeDoDbContext _context;
|
||||
|
||||
public TaskRunRepository(SqliteConnectionFactory factory) => _factory = factory;
|
||||
public TaskRunRepository(ClaudeDoDbContext context) => _context = context;
|
||||
|
||||
public async Task AddAsync(TaskRunEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = """
|
||||
INSERT INTO task_runs (id, task_id, run_number, session_id, is_retry, prompt,
|
||||
result_markdown, structured_output, error_markdown, exit_code,
|
||||
turn_count, tokens_in, tokens_out, log_path, started_at, finished_at)
|
||||
VALUES (@id, @task_id, @run_number, @session_id, @is_retry, @prompt,
|
||||
@result_markdown, @structured_output, @error_markdown, @exit_code,
|
||||
@turn_count, @tokens_in, @tokens_out, @log_path, @started_at, @finished_at)
|
||||
""";
|
||||
BindRun(cmd, entity);
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
_context.TaskRuns.Add(entity);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(TaskRunEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = """
|
||||
UPDATE task_runs SET session_id = @session_id,
|
||||
result_markdown = @result_markdown,
|
||||
structured_output = @structured_output,
|
||||
error_markdown = @error_markdown,
|
||||
exit_code = @exit_code,
|
||||
turn_count = @turn_count,
|
||||
tokens_in = @tokens_in,
|
||||
tokens_out = @tokens_out,
|
||||
finished_at = @finished_at
|
||||
WHERE id = @id
|
||||
""";
|
||||
cmd.Parameters.AddWithValue("@id", entity.Id);
|
||||
cmd.Parameters.AddWithValue("@session_id", (object?)entity.SessionId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@result_markdown", (object?)entity.ResultMarkdown ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@structured_output", (object?)entity.StructuredOutputJson ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@error_markdown", (object?)entity.ErrorMarkdown ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@exit_code", entity.ExitCode.HasValue ? entity.ExitCode.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@turn_count", entity.TurnCount.HasValue ? entity.TurnCount.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@tokens_in", entity.TokensIn.HasValue ? entity.TokensIn.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@tokens_out", entity.TokensOut.HasValue ? entity.TokensOut.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@finished_at", entity.FinishedAt.HasValue ? entity.FinishedAt.Value.ToString("o") : DBNull.Value);
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
_context.TaskRuns.Update(entity);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<TaskRunEntity?> GetByIdAsync(string runId, CancellationToken ct = default)
|
||||
public async Task<TaskRunEntity?> GetByIdAsync(string id, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT id, task_id, run_number, session_id, is_retry, prompt, result_markdown, structured_output, error_markdown, exit_code, turn_count, tokens_in, tokens_out, log_path, started_at, finished_at FROM task_runs WHERE id = @id";
|
||||
cmd.Parameters.AddWithValue("@id", runId);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||
if (!await reader.ReadAsync(ct)) return null;
|
||||
return ReadRun(reader);
|
||||
return await _context.TaskRuns.FirstOrDefaultAsync(r => r.Id == id, ct);
|
||||
}
|
||||
|
||||
public async Task<List<TaskRunEntity>> GetByTaskIdAsync(string taskId, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT id, task_id, run_number, session_id, is_retry, prompt, result_markdown, structured_output, error_markdown, exit_code, turn_count, tokens_in, tokens_out, log_path, started_at, finished_at FROM task_runs WHERE task_id = @task_id ORDER BY run_number";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||
var result = new List<TaskRunEntity>();
|
||||
while (await reader.ReadAsync(ct))
|
||||
result.Add(ReadRun(reader));
|
||||
return result;
|
||||
return await _context.TaskRuns
|
||||
.Where(r => r.TaskId == taskId)
|
||||
.OrderBy(r => r.RunNumber)
|
||||
.ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<TaskRunEntity?> GetLatestByTaskIdAsync(string taskId, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT id, task_id, run_number, session_id, is_retry, prompt, result_markdown, structured_output, error_markdown, exit_code, turn_count, tokens_in, tokens_out, log_path, started_at, finished_at FROM task_runs WHERE task_id = @task_id ORDER BY run_number DESC LIMIT 1";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||
if (!await reader.ReadAsync(ct)) return null;
|
||||
return ReadRun(reader);
|
||||
return await _context.TaskRuns
|
||||
.Where(r => r.TaskId == taskId)
|
||||
.OrderByDescending(r => r.RunNumber)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static void BindRun(SqliteCommand cmd, TaskRunEntity e)
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@id", e.Id);
|
||||
cmd.Parameters.AddWithValue("@task_id", e.TaskId);
|
||||
cmd.Parameters.AddWithValue("@run_number", e.RunNumber);
|
||||
cmd.Parameters.AddWithValue("@session_id", (object?)e.SessionId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@is_retry", e.IsRetry ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@prompt", e.Prompt);
|
||||
cmd.Parameters.AddWithValue("@result_markdown", (object?)e.ResultMarkdown ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@structured_output", (object?)e.StructuredOutputJson ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@error_markdown", (object?)e.ErrorMarkdown ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@exit_code", e.ExitCode.HasValue ? e.ExitCode.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@turn_count", e.TurnCount.HasValue ? e.TurnCount.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@tokens_in", e.TokensIn.HasValue ? e.TokensIn.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@tokens_out", e.TokensOut.HasValue ? e.TokensOut.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@log_path", (object?)e.LogPath ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@started_at", e.StartedAt.HasValue ? e.StartedAt.Value.ToString("o") : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@finished_at", e.FinishedAt.HasValue ? e.FinishedAt.Value.ToString("o") : DBNull.Value);
|
||||
}
|
||||
|
||||
private static TaskRunEntity ReadRun(SqliteDataReader r) => new()
|
||||
{
|
||||
Id = r.GetString(0),
|
||||
TaskId = r.GetString(1),
|
||||
RunNumber = r.GetInt32(2),
|
||||
SessionId = r.IsDBNull(3) ? null : r.GetString(3),
|
||||
IsRetry = r.GetInt32(4) != 0,
|
||||
Prompt = r.GetString(5),
|
||||
ResultMarkdown = r.IsDBNull(6) ? null : r.GetString(6),
|
||||
StructuredOutputJson = r.IsDBNull(7) ? null : r.GetString(7),
|
||||
ErrorMarkdown = r.IsDBNull(8) ? null : r.GetString(8),
|
||||
ExitCode = r.IsDBNull(9) ? null : r.GetInt32(9),
|
||||
TurnCount = r.IsDBNull(10) ? null : r.GetInt32(10),
|
||||
TokensIn = r.IsDBNull(11) ? null : r.GetInt32(11),
|
||||
TokensOut = r.IsDBNull(12) ? null : r.GetInt32(12),
|
||||
LogPath = r.IsDBNull(13) ? null : r.GetString(13),
|
||||
StartedAt = r.IsDBNull(14) ? null : DateTime.Parse(r.GetString(14), null, DateTimeStyles.RoundtripKind),
|
||||
FinishedAt = r.IsDBNull(15) ? null : DateTime.Parse(r.GetString(15), null, DateTimeStyles.RoundtripKind),
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user