Problem with VerticaCommand.Connection and .Transaction

It looks like there is a problem with the validation code in the setters of these two properties. If I set the connection and then the transaction, all is fine. But if I try to set the transaction first - it throws an InvalidOperationException with a message saying that the transaction's connection and the VerticaCommand's connection must be the same. It makes sense - the connection property is still null. It reveals a flaw in the base class DbCommand - allows dependency between the properties. The solution is simple - do it in the right order, however I do not have access to the calling code that does this. It works with SqlServer. My solution was to wrap the VerticaCommand class in MyVerticaCommand; allow for any transactions if the connection is null and store it in a temp. field; and when they set the connection - compare it with the Transaction's connection and if not the same - throw the exception, otherwise set both in the right order.

I am hoping that in spite of this being a small problem it can be fixed and I can get rid of the silly MyVerticaCommand class in my code.

Thanks

Val

Comments

  • Nimmi_guptaNimmi_gupta - Select Field - Employee
    Would it be possible to share the code sample to review? 
  • Sure. Basically my wrapper inherits from DbCommand and has a private field _command of type VerticaCommand - it is instantiated in the constructor of my class. The class has also a temporary field _transaction of type VerticaTransaction which temporarily caches the transaction object. Pretty much every virtual property and method delegates the call to the  field _command with exception of the properties Connection and Transaction where I fxied as explained above the problem with setting these properties in any order. I guess the right thing for the VerticaCommand is to do something similar: allow setting the transaction only if the connection is null or is enlisted in the same transaction; and also similarly allow setting the connection only if the transaction is null or equal to the transaction of the connection.

        class MyVerticaCommand : DbCommand, ICloneable
        {
            #region fields
            VerticaCommand _command;
            VerticaTransaction _transaction;
            #endregion
            public VerticaEFCommand()
            {
                _command = new VerticaCommand();
                _command.CommandType = CommandType.Text;
            }
            #region DbCommand overrides
            protected override DbConnection DbConnection
            {
                get { return _command.Connection as DbConnection; }
                set
                {
                    if (value == null)
                    {
                        _command.Connection = null;
                        return;
                    }
                    var conn = value as VerticaConnection;
                    if (conn == null)
                        throw new ArgumentException("Expected object of type VerticaConnection.", "value");
                    _command.Connection = conn;
                    if (_transaction != null)
                    {
                        if (_transaction.Connection != conn)
                            throw new InvalidOperationException("Transactions must share the same connection as commands.");
                        _command.Transaction = _transaction;
                        _transaction = null;
                    }
                }
            }
            protected override DbTransaction DbTransaction
            {
                get { return _command.Transaction; }
                set
                {
                    if (value == null)
                    {
                        _command.Transaction = null;
                        _transaction = null;
                        return;
                    }
                    var tran = value as VerticaTransaction;
                    if (tran == null)
                        throw new ArgumentException("Expected object of type VerticaTransaction.", "value");
                    if (tran.Connection != null  &&
                        _command.Connection == null)
                    {
                        // store the transaction temporarily in the local field until the client assigns a connection.
                        _transaction = tran;
                        return;
                    }
                    _command.Transaction = tran;
                }
            }
            public override void Cancel()
            {
                _command.Cancel();
            }
            public override string CommandText
            {
                get { return _executionStrategy.CommandText; }
                set { _executionStrategy.CommandText = value; }
            }
            public override int CommandTimeout
            {
                get { return _command.CommandTimeout; }
                set { _command.CommandTimeout = value; }
            }
            public override CommandType CommandType
            {
                get { return _command.CommandType; }
                set
                {
                    if (value != System.Data.CommandType.Text)
                        throw new NotSupportedException("This command type is not supported.");
                    _command.CommandType = value;
                }
            }
            public override bool DesignTimeVisible
            {
                get { return _command.DesignTimeVisible; }
                set { _command.DesignTimeVisible = value; }
            }
            public override UpdateRowSource UpdatedRowSource
            {
                get { return _command.UpdatedRowSource; }
                set { _command.UpdatedRowSource = value; }
            }
            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                if (disposing)
                {
                    _command.Dispose();
                    _executionStrategy.Dispose();
                }
            }
            #endregion
            #region ICloneable Members
            [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="N/A")]
            public object Clone()
            {
                var clone = new MyVerticaCommand
                {
                    _transaction         = this._transaction,
                    _command             = (VerticaCommand)this._command.Clone(),
                };
                foreach (VerticaParameter p in ParametersCollection)
                    clone.ParametersCollection.Add(p.Clone());
                return clone;
            }
            #endregion
        }

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file