我在后面创建了一个方法:
锁定桌子
从中读取价值
回写更新的值
解锁桌子
该代码适用于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.aspx和http://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/