插入记录会导致锁定

时间:2014-04-04 21:48:40

标签: c# sql ado.net sqlanywhere

当我使用SQL Anywhere 16 .net提供程序进行插入时,它会在表上创建共享锁。即使事后提交。我该如何预防呢? (或者我做错了什么?)

    DbCommand command = new SACommand();
    command.CommandTimeout = this.Timeout;
    bool mustCloseConnection = false;
    PrepareCommand(command, connection, null, commandType, commandText, commandParameters, ref mustCloseConnection);
    int num2 = command.ExecuteNonQuery();
    command.Parameters.Clear();
    if (mustCloseConnection)
    {
        connection.Close();
    }


    private void PrepareCommand(IDbCommand command, IDbConnection connection, IDbTransaction transaction, CommandType commandType, string commandText, IDataParameter[] commandParameters, ref bool mustCloseConnection)
    {
        if (command == null)
        {
            throw new ArgumentNullException("command");
        }
        if ((commandText == null) || (commandText.Length == 0))
        {
            throw new ArgumentNullException("commandText");
        }
        if (connection.State != ConnectionState.Open)
        {
            connection.Open();
            mustCloseConnection = true;
        }
        else
        {
            mustCloseConnection = false;
        }
        command.Connection = connection;
        command.CommandText = commandText;
        command.CommandTimeout = this.Timeout;
        if (transaction != null)
        {
            if (transaction.Connection == null)
            {
                throw new ArgumentException("The transaction was rollbacked or commited, please provide an open transaction.", "transaction");
            }
            command.Transaction = transaction;
        }
        command.CommandType = commandType;
        if (commandParameters != null)
        {
            AttachParameters(command, commandParameters);
        }
    }

3 个答案:

答案 0 :(得分:1)

您应该处置您的连接和命令对象。调用Dispose或将其包装在这样的using语句中:(例如,只输入代码)

using (IDBConnection con = new DbConnection())
{
  using (IDBCommand com = new DbCommand())
  {
    // do your sql stuff here
  }
  con.Close();
}

我在MSSQL中遇到过这样的错误,因为我没有处理我的命令对象。

答案 1 :(得分:1)

好的,让我们查看一些文档:

SQL Anywhere® Server - Programming > Using SQL in Applications > Controlling transactions in applications > Cursors and transactions
In general, a cursor closes when a COMMIT is performed. There are two exceptions to this behavior:

The close_on_endtrans database option is set to Off.

**A cursor is opened WITH HOLD**, which is the default with Open Client and JDBC.

If either of these two cases is true, the cursor remains open on a COMMIT.

那一个: http://infocenter.sybase.com/help/topic/com.sybase.help.sqlanywhere.12.0.1/pdf/dbprogramming12.pdf

Cursor behavior when opening cursors
You can configure the following aspects of cursor behavior when you open the cursor:
● Isolation level You can explicitly set the isolation level of operations on a cursor to be different
from the current isolation level of the transaction. To do this, set the isolation_level option.
● Holding By default, cursors in embedded SQL close at the end of a transaction. Opening a cursor
WITH HOLD allows you to keep it open until the end of a connection, or until you explicitly close it.
ADO.NET, ODBC, JDBC, and Open Client leave cursors open at the end of transactions by default

也许有一些更新鲜的文档,但我不知道有什么事情已被改变。 我几乎可以肯定你有WITH HOLD Cursor所以即使在COMMIT之后它仍然保持打开状态。

实际上你已经有了答案,只是没有解释为什么你应该关闭连接..

我希望这会有所帮助......

答案 2 :(得分:1)

如果您100%确定所有连接,命令,交易等都得到优雅处理,那么您可能需要检查实例化交易的方式。

Microsoft正确设计了TransactionScope默认构造函数,迟早会导致锁定(类似问题可能适用于其他事务类)。默认构造函数使用" Serializable隔离级别"这很容易造成锁定。

问题更严重:如果您实例化具有更好隔离级别的TransactionScope(例如,ReadCommitted),如果您的SQL操作失败并执行回滚,它将在内部再次将隔离级别更改为&# 34; Serializable",它将倾向于使用相同的事务实例锁定任何其他命令。

这被报告为微软的一个错误,而MS只是回复了典型的"嗯,如果这是一个错误,我们无法解决它,因为它可能影响那里的系统,为什么呢?您甚至使用默认构造函数创建TransactionScope实例?你不应该这样做#34;借口。

使用TransactionScope类的唯一安全方法是始终和每个时间使用显式所需的隔离级别来实例化它,这不会导致锁定(通常情况是使用ReadCommitted,但您可以使用其他)。

您可以使用此处http://blogs.msdn.com/b/dbrowne/archive/2010/06/03/using-new-transactionscope-considered-harmful.aspx?Redirected=true中建议的静态构建器:

public class TransactionUtils {
  public static TransactionScope CreateTransactionScope()
  {
    var transactionOptions = new TransactionOptions();
    transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
    transactionOptions.Timeout = TransactionManager.MaximumTimeout;
    return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
  }
}

或使用自定义包装类:

namespace System.Transactions {
    /**************************************************************************************************************
     * IMPORTANT: This class must ALWAYS be used instead of the regular "TransactionScope" class. This class
     * enforces having transactions that read "only committed" data.
     * 
     * This is because the implementation of TransactionScope is faulty (wrong design) and it gives issues because
     * it changes the connections available at the the connection pool. To read more, check this link:
     * http://blogs.msdn.com/b/dbrowne/archive/2010/05/21/using-new-transactionscope-considered-harmful.aspx
     * 
     * The namespace was set to "System.Transactions" in order to provide ease of use when updating legacy code
     * that was using the old class
     **************************************************************************************************************/
    public class SafeTransactionScope : IDisposable {
        private TransactionScope _transactionScope;
        private bool _disposed;

        #region Constructors
        public SafeTransactionScope()
            : this(TransactionManager.MaximumTimeout) {
        }

        public SafeTransactionScope(TimeSpan scopeTimeout)
            : this(TransactionScopeOption.Required, scopeTimeout) {
        }

        public SafeTransactionScope(TransactionScopeOption scopeOption)
            : this(scopeOption, TransactionManager.MaximumTimeout) {
        }

        public SafeTransactionScope(TransactionScopeOption scopeOption, TimeSpan scopeTimeout) {
            this._disposed = false;
            this._transactionScope = CreateTransactionScope(scopeOption, scopeTimeout);
        } 
        #endregion

        #region Disposing methods
        public void Dispose() {
            Dispose(true);

            // Use SupressFinalize in case a subclass 
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing) {
            if(!this._disposed) {
                if(disposing) {
                    if(this._transactionScope != null) {
                        this._transactionScope.Dispose();
                        this._transactionScope = null;
                    }
                }

                // Indicate that the instance has been disposed.
                this._disposed = true;
            }
        } 
        #endregion

        public void Complete() {
            if(this._disposed) {
                throw new ObjectDisposedException("SafeTransactionScope");
            }

            if(this._transactionScope == null) {
                // This should never happen
                throw new ObjectDisposedException("SafeTransactionScope._transactionScope");
            }

            this._transactionScope.Complete();
        }

        private static TransactionScope CreateTransactionScope(TransactionScopeOption scopeOption, TimeSpan scopeTimeout) {
            var transactionOptions = new TransactionOptions() {
                IsolationLevel = IsolationLevel.ReadCommitted,
                Timeout = scopeTimeout
            };

            return new TransactionScope(scopeOption, transactionOptions);
        }
    }
}