Please take this survey to help us learn more about how you use third party tools. Your input is greatly appreciated!

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

  • 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

Can't find what you're looking for? Search the Vertica Documentation, Knowledge Base, or Blog for more information.