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,102 +1,43 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClaudeDo.Data.Repositories;
|
||||
|
||||
public sealed class WorktreeRepository
|
||||
{
|
||||
private readonly SqliteConnectionFactory _factory;
|
||||
private readonly ClaudeDoDbContext _context;
|
||||
|
||||
public WorktreeRepository(SqliteConnectionFactory factory) => _factory = factory;
|
||||
|
||||
private static string ToDb(WorktreeState s) => s switch
|
||||
{
|
||||
WorktreeState.Active => "active",
|
||||
WorktreeState.Merged => "merged",
|
||||
WorktreeState.Discarded => "discarded",
|
||||
WorktreeState.Kept => "kept",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(s)),
|
||||
};
|
||||
|
||||
private static WorktreeState FromDb(string s) => s switch
|
||||
{
|
||||
"active" => WorktreeState.Active,
|
||||
"merged" => WorktreeState.Merged,
|
||||
"discarded" => WorktreeState.Discarded,
|
||||
"kept" => WorktreeState.Kept,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(s)),
|
||||
};
|
||||
public WorktreeRepository(ClaudeDoDbContext context) => _context = context;
|
||||
|
||||
public async Task AddAsync(WorktreeEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = """
|
||||
INSERT INTO worktrees (task_id, path, branch_name, base_commit, head_commit, diff_stat, state, created_at)
|
||||
VALUES (@task_id, @path, @branch_name, @base_commit, @head_commit, @diff_stat, @state, @created_at)
|
||||
""";
|
||||
cmd.Parameters.AddWithValue("@task_id", entity.TaskId);
|
||||
cmd.Parameters.AddWithValue("@path", entity.Path);
|
||||
cmd.Parameters.AddWithValue("@branch_name", entity.BranchName);
|
||||
cmd.Parameters.AddWithValue("@base_commit", entity.BaseCommit);
|
||||
cmd.Parameters.AddWithValue("@head_commit", (object?)entity.HeadCommit ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@diff_stat", (object?)entity.DiffStat ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@state", ToDb(entity.State));
|
||||
cmd.Parameters.AddWithValue("@created_at", entity.CreatedAt.ToString("o"));
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
_context.Worktrees.Add(entity);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<WorktreeEntity?> GetByTaskIdAsync(string taskId, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT task_id, path, branch_name, base_commit, head_commit, diff_stat, state, created_at FROM worktrees WHERE task_id = @task_id";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||
if (!await reader.ReadAsync(ct)) return null;
|
||||
return ReadWorktree(reader);
|
||||
return await _context.Worktrees.FirstOrDefaultAsync(w => w.TaskId == taskId, ct);
|
||||
}
|
||||
|
||||
public async Task UpdateHeadAsync(string taskId, string headCommit, string? diffStat, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "UPDATE worktrees SET head_commit = @head_commit, diff_stat = @diff_stat WHERE task_id = @task_id";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
cmd.Parameters.AddWithValue("@head_commit", headCommit);
|
||||
cmd.Parameters.AddWithValue("@diff_stat", (object?)diffStat ?? DBNull.Value);
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
await _context.Worktrees
|
||||
.Where(w => w.TaskId == taskId)
|
||||
.ExecuteUpdateAsync(s => s
|
||||
.SetProperty(w => w.HeadCommit, headCommit)
|
||||
.SetProperty(w => w.DiffStat, diffStat), ct);
|
||||
}
|
||||
|
||||
public async Task SetStateAsync(string taskId, WorktreeState state, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "UPDATE worktrees SET state = @state WHERE task_id = @task_id";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
cmd.Parameters.AddWithValue("@state", ToDb(state));
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
await _context.Worktrees
|
||||
.Where(w => w.TaskId == taskId)
|
||||
.ExecuteUpdateAsync(s => s.SetProperty(w => w.State, state), ct);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string taskId, CancellationToken ct = default)
|
||||
{
|
||||
await using var conn = _factory.Open();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "DELETE FROM worktrees WHERE task_id = @task_id";
|
||||
cmd.Parameters.AddWithValue("@task_id", taskId);
|
||||
await cmd.ExecuteNonQueryAsync(ct);
|
||||
await _context.Worktrees.Where(w => w.TaskId == taskId).ExecuteDeleteAsync(ct);
|
||||
}
|
||||
|
||||
private static WorktreeEntity ReadWorktree(SqliteDataReader r) => new()
|
||||
{
|
||||
TaskId = r.GetString(0),
|
||||
Path = r.GetString(1),
|
||||
BranchName = r.GetString(2),
|
||||
BaseCommit = r.GetString(3),
|
||||
HeadCommit = r.IsDBNull(4) ? null : r.GetString(4),
|
||||
DiffStat = r.IsDBNull(5) ? null : r.GetString(5),
|
||||
State = FromDb(r.GetString(6)),
|
||||
CreatedAt = DateTime.Parse(r.GetString(7)),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user