我在后面创建了一个方法:


锁定桌子
从中读取价值
回写更新的值
解锁桌子


该代码适用于Oracle。现在,我无法在SQL Server 2008上使用它。该方法在下面,执行我的解锁命令将导致带有文本的SqlException


  “ NOLOC”不是公认的表提示选项。如果打算用作
  表值函数或CHANGETABLE函数的参数,
  确保您的数据库兼容模式设置为90。


码:

public static int GetAndSetMaxIdTable(DbProviderFactory factory, DbConnection cnctn, DbTransaction txn, int tableId, string userName, int numberOfIds)
{
        bool isLocked = false;
        string sql = string.Empty;
        string maxIdTableName;

        if (tableId == 0)
            maxIdTableName = "IdMax";
        else
            maxIdTableName = "IdMaxTable";

        try
        {
            bool noPrevRow = false;
            int realMaxId;

            if (factory is OracleClientFactory)
                sql = string.Format("lock table {0} in exclusive mode", maxIdTableName);
            else if (factory is SqlClientFactory)
                sql = string.Format("select * from {0} with (TABLOCKX)", maxIdTableName);
            else
                throw new Exception(string.Format("Unsupported DbProviderFactory -type: {0}", factory.GetType().ToString()));

            using (DbCommand lockCmd = cnctn.CreateCommand())
            {
                lockCmd.CommandText = sql;
                lockCmd.Transaction = txn;
                lockCmd.ExecuteNonQuery();
                isLocked = true;
            }

            using (DbCommand getCmd = cnctn.CreateCommand())
            {
                getCmd.CommandText = CreateSelectCommand(factory, tableId, userName, getCmd, txn);

                object o = getCmd.ExecuteScalar();
                if (o == null)
                {
                    noPrevRow = true;
                    realMaxId = 0;
                }
                else
                {
                    realMaxId = Convert.ToInt32(o);
                }
            }

            using (DbCommand setCmd = cnctn.CreateCommand())
            {
                if (noPrevRow)
                    setCmd.CommandText = CreateInsertCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);
                else
                    setCmd.CommandText = CreateUpdateCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);

                setCmd.ExecuteNonQuery();
            }
            if (factory is OracleClientFactory)
                sql = string.Format("lock table {0} in share mode", maxIdTableName);
            else if (factory is SqlClientFactory)
                sql = string.Format("select * from {0} with (NOLOC)", maxIdTableName);

            using (DbCommand lockCmd = cnctn.CreateCommand())
            {
                lockCmd.CommandText = sql;
                lockCmd.Transaction = txn;
                lockCmd.ExecuteNonQuery();
                isLocked = false;
            }

            return realMaxId;
        }
        catch (Exception e)
        {
          ...
        }
}


那么,这里出了什么问题?此错误来自何处?服务器还是客户端?我从C代码复制了该语句,并且该代码应该在那里工作。不幸的是,我无法调试并检查它是否对我有用。

编辑:尝试锁定和解锁(不读取或更新)会导致相同的异常。

谢谢&BR-马蒂

最佳答案

TABLOCKX提示可以按预期锁定表,但是您不能手动将其解锁。锁将保留多长时间取决于您的交易级别。如果您的连接上没有活动的事务,则在执行SELECT时将保留该锁,然后将其丢弃。

如果要实现“锁定表->使用表进行某些操作->释放锁”的顺序,则需要实现与此T-SQL脚本等效的ADO.NET:

BEGIN TRAN
    SELECT TOP (1) 1 FROM myTable (TABLOCKX, KEEPLOCK)
    -- do something with the table
COMMIT -- This will release the lock, if there is no outer transaction present


您可以通过DbCommand对象执行“ BEGIN TRAN” /“ COMMIT”,也可以使用System.Data.SqlClient.SqlTransaction类启动事务并提交。

注意:仅当您的连接尚未加入事务时,此方法才有效! SQL Server不支持嵌套事务,因此COMMIT将不执行任何操作,并且将保持锁定状态。如果您已有事务在运行,则在事务完成之前无法释放锁定。在这种情况下,通过sp_getapplock / sp_releaseapplock进行的同步可能会有所帮助。

编辑:如果您想了解有关交易,锁定和阻止的知识,我推荐以下两个视频:http://technet.microsoft.com/en-us/sqlserver/gg545007.aspxhttp://technet.microsoft.com/en-us/sqlserver/gg508892.aspx

关于c# - 如何使用SqlClient在ADO.NET/C# 4.0中解锁SQL Server 2008表?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12648213/

10-15 00:00