[英]How to manually lock and unlock table such that inserts are prevented
下面的代碼有效,但不能防止其他用戶插入行並因此創建重復的ID。
自動更新並分配表的ID。 在下面的代碼中,我執行以下操作:
獲取下一個可用的ID(nextID)
將每個實體的ID設置為nextID ++
批量插入
如何鎖定表,以便在上述三個任務運行時其他用戶無法插入? 我看到過類似的問題,建議設置ISOLATIONLEVEL READCOMMITTED
但是我認為在獲取nextID時不會鎖定表。
public void BulkInsertEntities(List<Entity> entities)
{
if (entities == null)
throw new ArgumentNullException(nameof(entities));
string tableName = "Entities";
// -----------------------------------------------------------------
// Prevent other users from inserting (but not reading) here
// -----------------------------------------------------------------
long lastID = GetLastID(tableName);
entities.ForEach(x => x.ID = lastID++);
using (SqlConnection con = new SqlConnection(db.Database.GetDbConnection().ConnectionString))
{
con.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.KeepIdentity))
{
bulkCopy.DestinationTableName = tableName;
DataTable tbl = DataUtil.ToDataTable<Entity>(entities);
foreach (DataColumn col in tbl.Columns)
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
bulkCopy.WriteToServer(tbl);
}
}
// ---------------------------
// Allow other users to insert
// ---------------------------
}
protected long GetLastID(string tableName)
{
long lastID = 0;
using (var command = db.Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT IDENT_CURRENT('{tableName}') + IDENT_INCR('{tableName}')";
db.Database.OpenConnection();
lastID = Convert.ToInt64(command.ExecuteScalar());
}
return lastID;
}
對於具有可變性的類似於身份的功能,可以創建一個命名序列:
create sequence dbo.MySequence as int
...並且在表上具有默認約束: default(next value for dbo.MySequence)
。
這樣做的好處是,您可以“燒錄” ID並將其發送給客戶端,以便他們擁有可以放入其數據中的密鑰...然后,當數據預先填充時,不會造成危害,也不會造成污染。 它比身份字段花費更多的工作,但這並不可怕。 “刻錄”是指您可以隨時通過在任意位置調用next value for dbo.MySequence
來獲取新ID。 如果保留該值,則知道不會將其分配給表。 該表將獲得您之后的下一個值。 然后,您可以在閑暇時插入一行您已獲得並持有的價值...知道這是一個合法的鑰匙。
SQL Server調用應用程序鎖中有一項功能。 我很少看到它使用過,但是您的示例可能是合適的。 基本上,您的想法是將觸發器放在表上,這些表首先測試是否有出色的app_lock:
if ( applock_test( 'public', 'MyLock', 'Exclusive' ) = 1 )
begin
raiserror( ... )
return
--> or wait and retry
end
...並且長時間運行的進程不會被中斷,它會在開始時獲取該應用鎖,並在最后釋放該鎖:
exec @rc = get_applock @dbPrincipal='public', @resource='MyLock', @lockMode='Exclusive'
if ( @rc = 0 )
begin
--> got the lock, do the damage...
--> and then, after carefully handling the edge cases,
--> and making sure we dont skip the release...
exec release_applock @resource='MyLock' @dbPrincipal='public'
end
有很多變化。 基於會話的鎖定,可以在會話結束(當心連接池),超時,多種鎖定模式(共享,互斥等)和作用域鎖定(可能不適用於特權數據庫用戶)時自動釋放。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.