#c# #sql #entity-framework #concurrency
#c# #sql #entity-framework #параллелизм
Вопрос:
Я использую таблицу SQL в качестве очереди заданий, очень похожей на статью здесь: https://vladmihalcea.com/database-job-queue-skip-locked /
Моя проблема в том, что я использую Entity Framework 6 с кодом Database First, и, насколько я могу судить, EF6 не поддерживает команду skip locked. Вот мой класс таблиц, и я использую каждый компьютер в качестве рабочего для выполнения задачи, которую я ему передаю.
public partial class InProgress
{
public int ID { get; set; }
public string Task { get; set; }
public string Computer { get; set; }
public DateTime Date { get; set; }
}
Есть ли у кого-нибудь какой-нибудь код на C #, которым они могут поделиться, чтобы я мог убедиться, что ни один другой компьютер не может работать над той же задачей, что и другой компьютер одновременно?
ОБНОВЛЕНИЕ: я хочу уточнить, что я не делаю традиционную очередь, в которой вы постоянно добавляете и удаляете очередь или, в данном случае, таблицу. У меня есть таблица, содержащая список задач, и я постоянно выполняю задачи, над которыми работают несколько компьютеров, и когда они завершены, они обновляют столбец даты с указанием времени завершения. Сначала я работаю над задачами, у которых самая старая дата.
Вот некоторый псевдокод того, что я пытаюсь сделать, основываясь на предоставленной информации
create procedure usp_enqueuePending
@date datetime,
@task varchar(50),
@computer varchar(50)
as
set nocount on;
insert into InProgresses(Date, Task, Computer)
values (@date, @task, @computer);
go
create procedure usp_dequeuePending
as
set nocount on;
declare @now datetime;
set @now = getutcdate();
with cte as (
select top(1)
Task
from InProgresses with (rowlock, updlock, readpast)
where Date < @now
order by Date)
delete from cte
output deleted.Task;
go
using var context = new context();
var dequeuedItem = context.usp_dequeuePending(); // not sure how to convert this back to an InProgress class
// do work here I'm guessing
// add to the queue when finished with it??
context.usp_enqueuePending(DateTime.UtcNow, task, computer);
Ответ №1:
Вы можете писать пользовательские запросы в EF Core, смотрите Здесь . Итак, вы могли бы сделать что-то вроде этого:
dbContext.InProgress
.FromSqlRaw("SELECT * FROM model.InProgress WITH (rowlock, updlock, readpast)")
.Where(...) // do other LINQ stuff here
Это не очень красиво, но на данный момент я не знаю лучшего решения.
Комментарии:
1. Подсказка о блокировке для SQL Server будет
(rowlock, updlock, readpast)
, и вам понадобится транзакция. См., например: rusanu.com/2010/03/26/using-tables-as-queues2. @DavidBrowne-Microsoft Я думаю, что ваш метод был бы лучшим, но я не уверен, как вы будете преобразовывать выходные данные из предлагаемой транзакции обратно в класс InProgress. У вас есть пример кода на C # для того, что я изложил выше? Я обновил свой вопрос, включив дополнительную информацию, кстати
3. как насчет sth, подобного этому?
await using var transaction = ...; var item = await context.InProgress.FromRawSql(...).Where(...).OrderBy(...).FirstOrDefaultAsync(...); context.InProgress.Remove(item); await context.SaveChangesAsync(); await transaction.CommitAsync(); // use the item here...
4. вы также можете просто вызвать процедуру внутри
FromSqlRaw(...)
, поскольку, похоже, она у вас уже есть