簡體   English   中英

如何手動鎖定和解鎖表,以防止插入

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM