0

I need to do status check before insert data, but I found EF Core does not lock table from read even I setup isolationlevel.

Here is my code: I need to lock SignTicket table and SignTicketApproval table from read until commit, so that others will not get false data about ticket status. But even I setup the isolation level, I can still read table in SSMS.

I need a table-level lock, because this is a insert-only table, every operation must check the latest record to determine the status of ticket.

Thank you.

using var _context = new EFContext();

using var transaction = _context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);

var ticket = _context.SignTicket
                     .Include(x => x.SignTicketApproval)
                     .ThenInclude(x => x.Result)
                     .Single(x => x.Id == cancelSignTicketRequest.SignTicketId.ToLong());

if (_signTicketService.GetTicketStatus(ticket) != TicketStatus.Open)
{
    throw new SignTicketStatusException("Ticket status is not open", ticket);
}

// Create new fake approval request and set __Cancel as outcome
var utcNow = DateTime.UtcNow;

var cancelSignoff = new SignTicketApproval()
            {
                AssignedTo = cancelSignTicketRequest.CanceledBy,
                Description = "User cancel",
                CreateTime = utcNow,
                Result = new SignTicketApprovalResult()
                {
                    Canceled = true,
                    ApproverEmail = cancelSignTicketRequest.CanceledBy,
                    CreateTime = utcNow,
                    Outcome = ReservedButton.CancelButton,
                    Comment = cancelSignTicketRequest.Reason
                }
            };

ticket.SignTicketApproval.Add(cancelSignoff);

_context.SaveChanges();
transaction.Commit();
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
qwer11121
  • 138
  • 2
  • 15
  • Isolation level does not do table locks, it will lock rows that you read, but again it will not prevent other transactions from querying data or inserting new rows. (It can block inserts where keys are nominated and potentially in locked ranges) So for instance with Serializable isolation level, attempting an Update on that row after your breakpoint would be blocked, but reads aren't and updates on other rows aren't. When concurrency matters I would recommend using a concurrency timestamp/row-version that is checked before any change is accepted. – Steve Py Nov 16 '22 at 04:25
  • @StevePy Thank you for explaining the detail, could you give an example of " concurrency timestamp/row-version"? Does that implement in my code or on SQL Server? My problem is I can only perform Select and Insert to table. – qwer11121 Nov 16 '22 at 05:12
  • A concurrency token is applied to the table, often called a RowVersion using a Timestamp SQL column. SQL Server updates this automatically whenever a row is updated and EF can be configured to use it via the `[Timestamp]` attribute to be recognized as an auto-populated concurrency token. From there you can check the RowVersion value taken when the data was read against the RowVersion currently in the DB. If they differ then the DB was changed since your in-memory snapshot was taken. EF also checks if you use an `Update` with a detached entity. – Steve Py Nov 16 '22 at 07:30
  • You are right that the Transaction Isolation Level cannot lock the table. Found a post here and hope this helps. https://stackoverflow.com/questions/45917783/is-ef-can-lock-table – Charles Han Nov 16 '22 at 08:20

0 Answers0