Skip to main content

Problem with VerticaCommand.Connection and .Transaction

  • September 20, 2014
  • 2 replies
  • 16 views

Val_Melamed_1

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

2 replies

Nimmi_gupta
Forum|alt.badge.img
  • Participating Frequently
  • October 7, 2014
Would it be possible to share the code sample to review?

Val_Melamed
  • New Participant
  • October 30, 2014

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
    }