Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I lock a table on read, using Entity Framework?

I have a SQL Server (2012) which I access using Entity Framework (4.1). In the database I have a table called URL into which an independent process feeds new URLs. An entry in the URL table can be in state "New", "In Process" or "Processed".

I need to access the URL table from different computers, check for URL entries with status "New", take the first one and mark it as "In Process".

var newUrl = dbEntity.URLs.FirstOrDefault(url => url.StatusID == (int) URLStatus.New); if(newUrl != null) {     newUrl.StatusID = (int) URLStatus.InProcess;     dbEntity.SaveChanges(); } //Process the URL 

Since the query and update are not atomic, I can have two different computers read and update the same URL entry in the database.

Is there a way to make the select-then-update sequence atomic to avoid such clashes?

like image 216
Gilad Gat Avatar asked Nov 15 '12 18:11

Gilad Gat


People also ask

How do I lock a database table?

The table lock lasts until the end of the current transaction. To lock a table, you must either be the database owner or the table owner. Explicitly locking a table is useful to: Avoid the overhead of multiple row locks on a table (in other words, user-initiated lock escalation)

Can a view lock a table?

Table locks can be acquired for base tables or views. You must have the LOCK TABLES privilege, and the SELECT privilege for each object to be locked. For view locking, LOCK TABLES adds all base tables used in the view to the set of tables to be locked and locks them automatically.

How do I lock a table in SQL Server?

Expand server – management-currentActivity-expand Locks/object you can see locks by object information. Expand-server-management-double click Activity Monitor. on left side you have three options to choose from, select those options and you can see all the locks related information.

How can I lock a table in Oracle?

Use the LOCK TABLE statement to lock one or more tables, table partitions, or table subpartitions in a specified mode. This lock manually overrides automatic locking and permits or denies access to a table or view by other users for the duration of your operation.


2 Answers

I was only able to really accomplish this by manually issuing a lock statement to a table. This does a complete table lock, so be careful with it! In my case it was useful for creating a queue that I didn't want multiple processes touching at once.

using (Entities entities = new Entities()) using (TransactionScope scope = new TransactionScope()) {     //Lock the table during this transaction     entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)");      //Do your work with the locked table here...      //Complete the scope here to commit, otherwise it will rollback     //The table lock will be released after we exit the TransactionScope block     scope.Complete(); } 

Update - In Entity Framework 6, especially with async / await code, you need to handle the transactions differently. This was crashing for us after some conversions.

using (Entities entities = new Entities()) using (DbContextTransaction scope = entities.Database.BeginTransaction()) {     //Lock the table during this transaction     entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)");      //Do your work with the locked table here...      //Complete the scope here to commit, otherwise it will rollback     //The table lock will be released after we exit the TransactionScope block     scope.Commit(); } 
like image 174
jocull Avatar answered Oct 02 '22 02:10

jocull


The answer that @jocull provided is great. I offer this tweak:

Instead of this:

"SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)" 

Do this:

"SELECT TOP 0 NULL FROM MyTable WITH (TABLOCKX)" 

This is more generic. You can make a helper method that simply takes the table name as a parameter. No need to know of the data (aka any column names), and there is no need to actually retrieve a record down the pipe (aka TOP 1)

like image 32
Timothy Khouri Avatar answered Oct 02 '22 02:10

Timothy Khouri