Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用SqlClient在ADO.NET/C#4.0中解锁SQLServer2008表?_C#_Sql Server_Ado.net_Sqlclient - Fatal编程技术网

如何使用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代码中复制了这个语句,它应该在那里工作。不幸的是,我无法调试和检

我在以前创建了一个方法:

  • 锁上桌子
  • 从中读取值
  • 回写更新的值
  • 打开桌子
  • 该代码适用于Oracle。现在我无法让它在SQLServer2008上运行。该方法如下所示,执行我的解锁命令将导致一个带有文本的
    SqlException

    “NOLOC”不是可识别的表提示选项。如果它是作为一个 表值函数或CHANGETABLE函数的参数, 确保数据库兼容模式设置为90

    代码:

    那么这里出了什么问题?这个错误是从哪里来的?服务器还是客户端?我从C代码中复制了这个语句,它应该在那里工作。不幸的是,我无法调试和检查它是否适合我

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


    谢谢&BR-Matti

    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不支持嵌套事务,因此提交不会执行任何操作,并且会保留锁。如果事务已在运行,则在事务完成之前无法释放锁。在这种情况下,通过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