如何使用SqlClient在ADO.NET/C#4.0中解锁SQLServer2008表?
我在以前创建了一个方法:如何使用SqlClient在ADO.NET/C#4.0中解锁SQLServer2008表?,c#,sql-server,ado.net,sqlclient,C#,Sql Server,Ado.net,Sqlclient,我在以前创建了一个方法: 锁上桌子 从中读取值 回写更新的值 打开桌子 该代码适用于Oracle。现在我无法让它在SQLServer2008上运行。该方法如下所示,执行我的解锁命令将导致一个带有文本的SqlException: “NOLOC”不是可识别的表提示选项。如果它是作为一个 表值函数或CHANGETABLE函数的参数, 确保数据库兼容模式设置为90 代码: 那么这里出了什么问题?这个错误是从哪里来的?服务器还是客户端?我从C代码中复制了这个语句,它应该在那里工作。不幸的是,我无法调试和检
SqlException
:
“NOLOC”不是可识别的表提示选项。如果它是作为一个
表值函数或CHANGETABLE函数的参数,
确保数据库兼容模式设置为90
代码:
那么这里出了什么问题?这个错误是从哪里来的?服务器还是客户端?我从C代码中复制了这个语句,它应该在那里工作。不幸的是,我无法调试和检查它是否适合我
编辑:只是尝试锁定和解锁(不读取或更新)会导致相同的异常
谢谢&BR-MattiTABLOCKX提示按您的意愿锁定表格,但您无法手动解锁。锁保留的时间取决于您的事务级别。如果您的连接上没有活动事务,则在执行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不支持嵌套事务,因此提交不会执行任何操作,并且会保留锁。如果事务已在运行,则在事务完成之前无法释放锁。在这种情况下,通过sp_getapplock/sp_releaseapplock进行同步可能会有所帮助
编辑:如果您想了解事务、锁定和阻止,我建议您观看以下两个视频:以下是我根据TToni的答案编写的SqlClient一个表的答案:
public static int GetAndSetMaxIdTable(DbProviderFactory factory, DbConnection cnctn, DbTransaction txn, int numberOfIds)
{
bool noPrevRow = false;
int realMaxId;
using (DbCommand getCmd = cnctn.CreateCommand())
{
getCmd.CommandText = "SELECT MaxId FROM IdMax WITH (TABLOCKX)"
getCmd.Transaction = 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();
}
return realMaxId;
}
我是这样的:
...
try
{
using (txn = cnctn.BeginTransaction())
{
oldMaxId = GetAndSetMaxIdTable(factory, cnctn, txn, 5);
for (i = 0; i < 5; i++)
{
UseNewIdToInsertStuff(factory, cnctn, txn, oldMaxId + i + 1)
}
txn.Commit();
return true;
}
}
catch (Exception e)
{
// don't know if this is needed
if (txn != null && cnctn.State == ConnectionState.Open)
txn.Rollback();
throw e;
}
...
-m首先,这是不可能的。您真的想将所有其他潜在的进程锁定在整个表之外吗?这样做的通常方法是在事务中,它可以在Oracle和SQL Server上工作。如果您想沿着这条路走下去,您就不需要Oracle或SQL Server特定的SQL,而通过IDbConnection、IDbCommand等,您可以摆脱大部分If-Oracle的东西,并且非常接近于不依赖于提供程序。是,必须锁定尝试访问表的每个客户端。我想先完成这项工作,但我也愿意接受更复杂的方法。如果你不知道我为什么会得到这个异常,你可以发布一个例子,说明如何处理这个事务。实际上,我已经在这里使用了一个事务,但显然你建议的方法是smthn else。如果你总是锁定整个表,这将是一个缓慢的应用程序。@horse:你能提供一些具体的替代方案吗?另一个表只有一行,其他表可能有30行。两者都有3列。此外,ID很少被获取。对整体性能没有任何影响。那么,即使您提出了一种新的机制,在所有这些不同的系统中实际实现它也将是一个太远的步骤,可能需要几个步骤。看起来这是你要么生活在一起,要么开始设计的遗留功能之一。对于后者,当几行聪明的代码可以修复时,就几乎是不可能的了。这太狡猾了,我不能说我会想到这一点,但我会试图以任何方式设计出独占锁的需要。@TToni:谢谢你的回答!我尝试在明天的工作中删除“解锁”部分并提交/回滚DbTransaction。很高兴知道这个限制,因为它是一个。谢谢你把它添加到这里,这样我就不必怀疑出了什么问题。当我能尝试的时候,我接受这个。再次感谢!我将发布一个替代方案,给我一个moI限制自己立即锁定的问题,但当然一般来说,你应该尽量避免过度锁定。独占表锁很少真正需要。我可以用两个方面的信息来详细说明:您希望通过锁定来防止什么样的“坏”行为?既然您似乎有一个外部事务,那么它运行在什么样的序列化级别上。哦,数据库中的ALLOW_SNAPSHOT_ISOLATION或READ_COMMITTED_SNAPSHOT是否设置为ON?所有这些都会影响事务中的SQL Server锁定行为(请参见答案中的视频)。在SQL Server中生成顺序标识值而不出现锁定问题的“正确”方法是使用标识列。我不知道这对你是否有效,但在这种情况下,请看这里:
...
try
{
using (txn = cnctn.BeginTransaction())
{
oldMaxId = GetAndSetMaxIdTable(factory, cnctn, txn, 5);
for (i = 0; i < 5; i++)
{
UseNewIdToInsertStuff(factory, cnctn, txn, oldMaxId + i + 1)
}
txn.Commit();
return true;
}
}
catch (Exception e)
{
// don't know if this is needed
if (txn != null && cnctn.State == ConnectionState.Open)
txn.Rollback();
throw e;
}
...
SELECT MaxId from IdMax WHERE ... FOR UPDATE OF MaxId