Можно ли использовать Database First EF 6 с командой SKIP LOCKED?

#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-queues

2. @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(...) , поскольку, похоже, она у вас уже есть