@Pat Moran I did some digging and came across https://stackoverflow.com/a/53403274/10987310 and I think this is what's happening to you. SERIALIZABLE will only apply the lock when UPDATE/INSERT is executed and before that happens, your subsequent thread has already came in and read the row.
It's still a concurrency issue though and you can employ those tactics such as a rowversion
field for concurrency detection.
You could consider something like...
using (CelticPoolsServiceContext db = new CelticPoolsServiceContext())
{
using (DbContextTransaction transaction = db.Database.BeginTransaction())
{
var lquery = from t in db.WindfallTickets
where (t.Status == Constants.TicketInitialStatus) && (t.CompetitionId == compid)
orderby t.IntA ascending
select t;
var next = lquery.FirstOrDefault<WindfallTicket>;
if (next != null)
{
try
{
tno = firstTicketnoStr = next.TicketNo;
next.Status = Constants.TicketPurchaseInitiated;
CopyTicketInfo(next, ticket);
next.Purchased = DateTime.UtcNow;
next.TransactionId = ticket.Email;
db.SaveChanges();
transaction.Commit();
epti = next.TicketNo;
}
catch (DbUpdateConcurrencyException)
{
transaction.RollBack();
}
}
}
}
You could also do employ a 'IsBusy' flag, something like...
var lquery = from t in db.WindfallTickets
where (t.Status == Constants.TicketInitialStatus) && (t.CompetitionId == compid) && !IsPending
orderby t.IntA ascending
select t;
var next = lquery.FirstOrDefault<WindfallTicket>;
if (next != null)
{
next.IsPending = true;
db.SaveChanges();
try
{
Another thing I can suggest is to leverage using a thread safe list like ConcurrentQueue<T> and your method above simply reads from this global list as a background service.