/// <summary> /// Returns a new ad-hoc record containing the changes from this record compared against the other record given. /// <para>Strict mode can be requeste, in which case all columns in both records are taken in consideration.</para> /// <para>When not in strict mode, only the columns of the source record are compared.</para> /// </summary> /// <param name="other">The other record to use in the comparison.</param> /// <param name="strict">True to activate strict mode, false otherwise.</param> /// <returns>A new record containing the changes and its schema, or null if no changes were detected.</returns> public KRecord Changes( KRecord other, bool strict = false ) { if( this._Schema == null ) throw new InvalidOperationException( "This record's schema is null." ); if( this.Schema.Count < 0 ) throw new InvalidOperationException( "This record's schema is disposed." ); if( other == null ) throw new ArgumentNullException( "other", "Other record cannot be null." ); if( other.Schema == null ) throw new InvalidOperationException( "Other record's schema is null." ); if( other.Schema.Count < 0 ) throw new InvalidOperationException( "Other record's schema is disposed." ); // If any record is empty... if( this.Schema.Count == 0 ) { if( other.Schema.Count == 0 ) return null; // Both are empty records... if( !strict ) return null; // We have no changes to inform about... return other.Clone( cloneSchema: true ); // All other contents are consideres as changed... } if( other.Schema.Count == 0 ) { if( !strict ) return null; // We have no changes to inform about... return this.Clone( cloneSchema: true ); // All other contents are consideres as changed... } // Preparing for returning a new record containing the changes... KSchema schema = new KSchema( _Schema.CaseSensitiveNames ); List<object> values = new List<object>(); bool anyTable = strict ? false : true; // First round: using this record's columns... for( int thisIx = 0; thisIx < this.Schema.Count; thisIx++ ) { var thisMeta = this.Schema[thisIx]; var otherMeta = other.Schema.Find( thisMeta.BaseTableName, thisMeta.ColumnName, anyTable, raise: false ); if( otherMeta == null ) { // Columns not found in other are considered only in strict mode... if( strict ) { object thisValue = this[thisIx]; schema.Add( thisMeta.Clone() ); values.Add( TypeHelper.TryClone( thisValue ) ); } } else { // Only annotating when their contents are not equivalent... int otherIx = other.Schema.IndexOf( otherMeta ); object thisValue = this[thisIx]; object otherValue = other[otherIx]; bool eq = TypeHelper.AreEquivalent( thisValue, otherValue ); if( !eq ) { schema.Add( thisMeta.Clone() ); values.Add( TypeHelper.TryClone( thisValue ) ); } } } // Second round: in strict we need to consider also the columns in other that do not appear in this... if( strict ) { for( int otherIx = 0; otherIx < other.Schema.Count; otherIx++ ) { var otherMeta = other.Schema[otherIx]; var thisMeta = this.Schema.Find( otherMeta.BaseTableName, otherMeta.ColumnName, anyTable: false, raise: false ); if( thisMeta == null ) { // Adding from a new column in the other record... var temp = schema.Find( otherMeta.BaseTableName, otherMeta.ColumnName, anyTable: true, raise: false ); if( temp == null ) { // Avoiding exception per duplicate columns... object otherValue = other[otherIx]; schema.Add( otherMeta.Clone() ); values.Add( TypeHelper.TryClone( otherValue ) ); } } } } // If no changes return null to facilitate comparisons... if( schema.Count == 0 ) { values = null; schema.Dispose(); schema = null; return null; } // Otherwise, generating the actual record containing the changes... KRecord record = new KRecord(); record._Schema = schema; record._Columns = values.ToArray(); return record; }
void Invoke( bool iterable ) { // Opening if needed... if( !_Link.IsDbOpened ) { _Link.DbOpen(); _LinkOpenedBySurrogate = true; } // Creating the database command and its parameters... IDbCommand cmd = _Link.DbConnection.CreateCommand(); cmd.CommandText = _Command.CommandText( iterable ); string nullStr = _Command.Parser.Parse( null, nulls: true ); // To substitute parameters with NULL values... foreach( var par in _Command.Parameters ) { if( par.Value == null ) cmd.CommandText = cmd.CommandText.Replace( par.Name, nullStr ); else { IDataParameter dbpar = cmd.CreateParameter(); dbpar.ParameterName = par.Name; dbpar.Value = _Link.TransformParameterValue( par.Value ); cmd.Parameters.Add( dbpar ); } } // Setting the transaction... Transaction scope = Transaction.Current; if( scope != null ) { } // Managed by TransactionScope else { IDbTransaction tx = null; // Using the current connection's transaction, if exists... var innerConnection = TypeHelper.GetElementValue( _Link.DbConnection, "InnerConnection" ); if( innerConnection != null ) { var currentTransaction = TypeHelper.GetElementValue( innerConnection, "CurrentTransaction" ); if( currentTransaction != null ) { var parent = TypeHelper.GetElementValue( currentTransaction, "Parent" ); if( parent != null ) tx = (IDbTransaction)parent; } } if( tx != null ) cmd.Transaction = tx; } // Execute if NON-QUERY... if( !iterable ) { try { _NonQueryResult = cmd.ExecuteNonQuery(); } catch { throw; } finally { cmd.Dispose(); cmd = null; } return; } // OTHERWISE, we need to capture the datareader and the schema returned from the database... try { _DataReader = cmd.ExecuteReader( CommandBehavior.KeyInfo ); } catch { throw; } finally { cmd.Dispose(); cmd = null; } // Creating the schema... DataTable table = _DataReader.GetSchemaTable(); if( table == null ) throw new InvalidOperationException( "Cannot obtain schema table for command: " + cmd ); string tablename = _Command is IKTableNameProvider ? ( (IKTableNameProvider)_Command ).TableName : null; _Schema = new KSchema( _Link.DbCaseSensitiveNames ); for( int i = 0; i < table.Rows.Count; i++ ) { DataRow row = table.Rows[i]; string meta = null; object value = null; bool hidden = false; if( table.Columns.Contains( "IsHidden" ) ) { value = row[table.Columns["IsHidden"]]; if( !( value is DBNull ) ) hidden = (bool)value; } if( hidden ) continue; KMetaColumn column = new KMetaColumn(); for( int j = 0; j < table.Columns.Count; j++ ) { meta = table.Columns[j].ColumnName; value = row[j] is DBNull ? null : row[j]; if( value != null ) column[meta] = value; } if( column.BaseTableName == null && tablename != null ) column.BaseTableName = tablename; DEBUG.WriteLine( "-- Schema Column: {0}", column ); _Schema.Add( column ); } table.Dispose(); table = null; // Setting the aliases if needed... if( _Command is IKTableAliasListProvider ) _Schema.TableAliasList.AddRange( ( (IKTableAliasListProvider)_Command ).TableAliasList ); // And finishing with the schema... _Schema.Sealed = true; }