Files
IM_NEW/IM.InitCommon/Management/ReceiptStore.cs
T

30 lines
1.8 KiB
C#

using System.Data;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
namespace IM.InitCommon.Management;
public static class ReceiptStore
{
public static async Task<ActionReceipt> Execute(DbContext db, InternalAction command, Func<Task<ActionReceipt>> apply, CancellationToken ct)
{
await using var tx = await db.Database.BeginTransactionAsync(IsolationLevel.Serializable, ct);
var connection = db.Database.GetDbConnection();
await using var read = connection.CreateCommand(); read.Transaction = tx.GetDbTransaction();
var owner = db.GetType().Name;
read.CommandText = "SELECT Payload FROM management_receipts WHERE Owner = @owner AND Id = @id FOR UPDATE";
var ownerParameter = read.CreateParameter(); ownerParameter.ParameterName = "@owner"; ownerParameter.Value = owner; read.Parameters.Add(ownerParameter);
var parameter = read.CreateParameter(); parameter.ParameterName = "@id"; parameter.Value = command.Id.ToString(); read.Parameters.Add(parameter);
var existing = await read.ExecuteScalarAsync(ct);
if (existing is string json) {
var saved = JsonSerializer.Deserialize<SavedReceipt>(json)!;
if (saved.Command != command) throw new InvalidOperationException("操作 ID 已被其他请求使用");
await tx.CommitAsync(ct); return saved.Result;
}
var receipt = await apply(); await db.SaveChangesAsync(ct);
await db.Database.ExecuteSqlInterpolatedAsync($"INSERT INTO management_receipts (Owner, Id, Payload, CreatedAt) VALUES ({owner}, {command.Id.ToString()}, {JsonSerializer.Serialize(new SavedReceipt(command, receipt))}, {DateTime.UtcNow})", ct);
await tx.CommitAsync(ct); return receipt;
}
private record SavedReceipt(InternalAction Command, ActionReceipt Result);
}