如何手动锁定和解锁表,以防止插入

时间:2019-03-14 15:04:17

标签: c# sql sql-server

以下代码有效,但不能防止其他用户插入行并因此创建重复的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;
}

1 个答案:

答案 0 :(得分:0)

对于具有可变性的类似于身份的功能,可以创建一个命名序列:

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

有很多变化。基于会话的锁,可以在会话结束(当心连接池),超时,多种锁模式(共享,互斥等)和作用域锁(可能不适用于特权数据库用户)时自动释放。 / p>