30 lines
1.8 KiB
C#
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);
|
|
}
|