Ich habe ein wenig Code, den ich verwende, um Elemente einzeln aus einer Datenbank zu ziehen. Bei jedem Abruf aktualisiert es den Status der Zeile, um anzuzeigen, dass sie abgerufen wurde, damit dieselbe Zeile nicht mehrmals abgerufen wird.
Der folgende Code wird von einem Back-End-Dienst ausgeführt, der eine Web-API bereitstellt. Manchmal, wenn mehrere Anfragen eingehen, wird dieselbe Zeile zurückgegeben (Aufgaben mit derselben ID).
Ich hatte den Eindruck, dass eine Transaktion um es herum bedeuten würde, dass eine Aktualisierung von einem der Läufe verhindern würde, dass die Zeile von einer zweiten Abfrage zurückgegeben wird.
Jede Hilfe wäre willkommen.
public async Task<TaskDetail> GetTask()
{
using (var db = new SqlConnection(""))
{
using (var tran = db.BeginTransaction())
{
try
{
var sql = $@"
SELECT TOP 1 * FROM
(SELECT TOP 150 t.*
FROM Task t
INNER JOIN TaskStatus ts ON t.Id = ts.TaskId AND ts.Status = @taskStatus) t
ORDER BY NEWID();";
var chosen = await db
.QuerySingleOrDefaultAsync<TaskDetail>(
sql,
param: new
{
taskStatus = TaskStatusEnum.Ready
},
transaction: tran
);
if (chosen == null)
{
throw new InvalidOperationException();
}
var expiry = await db.ExecuteAsync("UPDATE TaskStatus SET Status = @status WHERE TaskId = @taskId", new {status = TaskStatusEnum.Done, taskId = chosen.TaskId}, tran);
tran.Commit();
return chosen;
}
catch
{
tran.Rollback();
throw;
}
}
}
}
Select Query blockiert keine Zeilen von anderen Transaktionen, zwei select in verschiedenen Transaktionen werden gleichzeitig ausgeführt. Sie können versuchen, eine Sitzungs-ID für die Zeile festzulegen und sie dann auszuwählen.
EDIT : hoffe diese Hilfe, wenn Update ausgeführt wird, sollte es die Zeile für andere Transaktionen blockieren
public async Task<TaskDetail> GetTask()
{
using (var db = new SqlConnection(""))
{
using (var tran = db.BeginTransaction())
{
try
{
var mySessionId = Guid.NewGuid();
var sql = $@"
UPDATE TaskStatus SET Status = @status, SessionId = @mySessionId WHERE TaskId in
(SELECT TOP 1
t.Id
FROM Task t
INNER JOIN TaskStatus ts ON t.Id = ts.TaskId AND ts.Status = @taskStatus ORDER BY NEWID());";
await db
.QuerySingleOrDefaultAsync<TaskDetail>(
sql,
param: new
{
taskStatus = TaskStatusEnum.Ready
status = TaskStatusEnum.InProgress,
mySessionId = mySessionId
},
transaction: tran
);
var sql = $@"
SELECT TOP 150 t.*
FROM Task t
INNER JOIN TaskStatus ts ON t.Id = ts.TaskId AND ts.Status = @taskStatus
WHERE ts.SessionId = @mySessionId;";
var chosen = await db
.QuerySingleOrDefaultAsync<TaskDetail>(
sql,
param: new
{
taskStatus = TaskStatusEnum.InProgress,
mySessionId = mySessionId
},
transaction: tran
);
if (chosen == null)
{
throw new InvalidOperationException();
}
var expiry = await db.ExecuteAsync("UPDATE TaskStatus SET Status = @status WHERE TaskId = @taskId", new {status = TaskStatusEnum.Done, taskId = chosen.TaskId}, tran);
tran.Commit();
return chosen;
}
catch
{
tran.Rollback();
throw;
}
}
}
}