/*=========================*/
        #endregion

        #region Internal Methods
        /*=========================*/

        /// <summary>
        /// Fills the item with data retrieved from the data source using the select command.
        /// </summary>
        protected void Bind()
        {
            // Cannot bind when the table is not managed inside the item
            if (_owner != null)
            {
                throw new InvalidOperationException("Cannot independently bind an item that is part of a collection");
            }

            if (DataManager.ProxyMode)
            {
                ProxyRequestAction action = ProxyClient.Request.AddAction(this, MethodInfo.GetCurrentMethod());
                action.OnComplete = delegate()
                {
                    DataTable table = ProxyClient.Result[action].GetData <DataTable>("_table");
                    _table = table;
                    _row   = table.Rows[0];

                    // TODO: deal with any field backups and shit
                };
            }
            else
            {
                // Initialize the table
                if (_table == null)
                {
                    _table = new DataTable("_table");
                }
                else
                {
                    _table.Rows.Clear();
                }

                SqlDataAdapter adapter = DataManager.CreateAdapter(this);

                ConnectionKey ckey = null;
                try
                {
                    ckey = DataManager.Current.OpenConnection(this);

                    // Fill the table
                    adapter.Fill(this._table);
                }
                finally
                {
                    DataManager.Current.CloseConnection(ckey);
                }

                if (_table.Rows.Count < 1)
                {
                    throw new DataException("Binding operation did not return data");
                }

                _row = _table.Rows[0];

                if (ProxyServer.InProgress)
                {
                    ProxyServer.Current.Result[ProxyServer.Current.CurrentAction].AddData("_table", _table);
                }
            }
        }
        /*=========================*/
        #endregion

        #region Internal Methods
        /*=========================*/

        /// <summary>
        /// Populates the collection with DataItem objects by binding to the data source.
        /// </summary>
        ///
        /// <remarks>
        /// Calling this method clears the inner table before re-populating the list, which means
        /// items contained in the collection before the binding call are disassociated from the collection
        /// (their <c>DataState</c> changes to <c>DataRowState.Detached</c>). Changes to detached items
        /// will not be saved when the collection is saved (unless they are re-added).
        /// </remarks>
        protected void Bind()
        {
            // Clear the table
            _table.Rows.Clear();

            // Clear items
            _innerList.Clear();
            if (_hash != null)
            {
                _hash.Clear();
            }

            if (!DataManager.ProxyMode)
            {
                // Attach an event handler to the table so we can create DataItems for each row added
                DataRowChangeEventHandler addedHandler = new DataRowChangeEventHandler(_table_RowChanged);
                _table.RowChanged += addedHandler;

                SqlDataAdapter adapter = DataManager.CreateAdapter(this);
                ConnectionKey  ckey    = null;
                try
                {
                    ckey = DataManager.Current.OpenConnection(this);

                    // Fill the table
                    adapter.Fill(this._table);
                }
                finally
                {
                    DataManager.Current.CloseConnection(ckey);
                }

                // Detach the event so it doesn't get raised in future changes
                _table.RowChanged -= addedHandler;

                if (ProxyServer.InProgress)
                {
                    ProxyServer.Current.Result[ProxyServer.Current.CurrentAction].AddData("_table", _table);
                }
            }
            else
            {
                ProxyRequestAction action = ProxyClient.Request.AddAction(this, MethodInfo.GetCurrentMethod());
                action.OnComplete = delegate()
                {
                    DataTable table = ProxyClient.Result[action].GetData <DataTable>("_table");
                    _table = table;

                    // Create new items for each row using the existing handler
                    foreach (DataRow row in _table.Rows)
                    {
                        _table_RowChanged(_table, new DataRowChangeEventArgs(row, DataRowAction.Add));
                    }
                };
            }
        }
        /// <summary>
        /// Invoked when the item needs to be saved to the data source in independent mode.
        /// </summary>
        ///
        /// <remarks>
        /// The base implementation of this method uses a DataAdapter with the item's UpdateCommand
        /// in order to save it's fields to the database. Derived classes can override this method to add additional
        /// checks or operations before and after the save, or to change the saving method altogether.
        /// </remarks>
        protected virtual void OnSave()
        {
            // Cannot update when the table is not managed inside the item
            if (_owner != null)
            {
                throw new InvalidOperationException("Cannot independently save an item that is part of a collection");
            }

            // Rather than just allowing the adapter to skip the row, throw and exception to notify the consumer
            if (this.DataState != DataRowState.Added &&
                this.DataState != DataRowState.Modified &&
                this.DataState != DataRowState.Unchanged
                )
            {
                throw new InvalidOperationException("Cannot save an item with the DataState of " + this.DataState.ToString());
            }

            if (DataManager.ProxyMode)
            {
                ProxyRequestAction action = ProxyClient.Request.AddAction(this, MethodInfo.GetCurrentMethod());
                action.OnComplete = delegate()
                {
                    DataTable table = ProxyClient.Result[action].GetData <DataTable>("_table");
                    _table = table;
                    _row   = table.Rows[0];

                    // TODO: deal with any field backups and shit
                };
            }
            else
            {
                // Create an adapter
                SqlDataAdapter adapter = DataManager.CreateAdapter(this);

                ConnectionKey ckey = null;
                using (adapter)
                {
                    try
                    {
                        ckey = DataManager.Current.OpenConnection(this);
                        adapter.Update(_table);
                    }
                    finally
                    {
                        DataManager.Current.CloseConnection(ckey);
                    }
                }

                if (ProxyServer.InProgress)
                {
                    ProxyServer.Current.Result[ProxyServer.Current.CurrentAction].AddData("_table", _table);
                }
            }
        }
        /// <summary>
        /// Handles saving the collection's items to the data source.
        /// </summary>
        ///
        /// <remarks>
        /// The base implementation of this method uses a DataAdapter with the collection's associated commands
        /// in order to save the contained records to the database. Derived classes can override this method to add additional
        /// checks or operations before and after the save, or to change the saving method altogether.
        /// </remarks>
        protected virtual void OnSave()
        {
            // Don't perform the save if there are no changes
            if (_table.GetChanges() == null)
            {
                return;
            }

            if (!DataManager.ProxyMode)
            {
                // Create an adapter
                SqlDataAdapter adapter = DataManager.CreateAdapter(this);

                // Vars for proxy operations
                DataTable updatedRowsTable = null;
                SqlRowUpdatedEventHandler updatedHander = null;

                if (ProxyServer.InProgress)
                {
                    updatedRowsTable = _table.Clone();
                    updatedHander    = delegate(object sender, SqlRowUpdatedEventArgs e)
                    {
                        if (e.StatementType != StatementType.Insert && e.StatementType != StatementType.Update)
                        {
                            return;
                        }

                        // Store and row that has been inserted or updated
                        updatedRowsTable.ImportRow(e.Row);
                    };

                    adapter.RowUpdated += updatedHander;
                }

                ConnectionKey ckey = null;
                using (adapter)
                {
                    try
                    {
                        ckey = DataManager.Current.OpenConnection(this);
                        adapter.Update(_table);
                    }
                    finally
                    {
                        DataManager.Current.CloseConnection(ckey);
                    }
                }

                // Wrap up the proxy operation
                if (ProxyServer.InProgress)
                {
                    adapter.RowUpdated -= updatedHander;
                    ProxyServer.Current.Result[ProxyServer.Current.CurrentAction].AddData("updated", updatedRowsTable);
                }
            }
            else
            {
                ProxyRequestAction action = ProxyClient.Request.AddAction(this, MethodInfo.GetCurrentMethod());
                action.OnComplete = delegate()
                {
                    DataTable updated      = ProxyClient.Result[action].GetData <DataTable>("updated");
                    bool      usingInnerID = updated.Columns.Contains(Const.InnerIDColumn);
                    if (!usingInnerID)
                    {
                        throw new Exception("not using inner ID!!!");
                    }

                    // Go over each row and find matching rows
                    for (int i = 0; i < updated.Rows.Count; i++)
                    {
                        bool    merged     = false;
                        DataRow updatedRow = updated.Rows[i];
                        object  testID     = usingInnerID ? updatedRow[Const.InnerIDColumn] : null;
                        if (testID is int)
                        {
                            int       innerID = (int)testID;
                            DataRow[] rs      = _table.Select(String.Format("{0} = {1}", Const.InnerIDColumn, innerID));
                            if (rs.Length > 0)
                            {
                                // If a row with a matching innerID is found, merge the values and accept changes
                                DataRow row = rs[0];
                                row.ItemArray = updatedRow.ItemArray;
                                row.AcceptChanges();
                                merged = true;
                            }
                        }

                        // TODO: make sure we shouldn't be clearing the table before adding unidentified rows
                        if (!merged)
                        {
                            // Insert the new row at the same index
                            DataRow newRow = _table.NewRow();
                            newRow.ItemArray = updatedRow.ItemArray;
                            _table.Rows.InsertAt(newRow, i);

                            DataItem newItem = NewItem(newRow);
                            _innerList.Insert(i, newItem);

                            if (_hash != null)
                            {
                                _hash.Add(GetPrimaryKeyValue(newItem), newItem);
                            }
                        }
                    }
                };
            }
        }
        /// <summary>
        /// Runs the stand-alone SELECT command to retrieve fields that were not present when the item was
        /// created (in either independent or collection-owned mode).
        /// </summary>
        public virtual void RetrieveMissingFields()
        {
            if (DataManager.ProxyMode)
            {
                ProxyRequestAction action = ProxyClient.Request.AddAction(this, MethodInfo.GetCurrentMethod());
                action.OnComplete = delegate()
                {
                    DataTable table = ProxyClient.Result[action].GetData <DataTable>("_table");
                    _table = table;
                    _missingFields.Clear();

                    // When no owner just swap tables,
                    if (_owner == null)
                    {
                        _row = table.Rows[0];
                    }
                    else
                    {
                        _retrievedFieldRow = table.Rows[0];
                    }
                };
            }
            else
            {
                if (SelectCommand == null)
                {
                    throw new InvalidOperationException("There is no defined SELECT command so missing fields cannot be retrieved.");
                }

                // Only do something if there are missing fields to complete
                if (_missingFields.Count > 0 && _selectCmd != null)
                {
                    ConnectionKey key = null;

                    // Gets parameter values
                    foreach (SqlParameter param in SelectCommand.Parameters)
                    {
                        if (param.SourceColumn != null && param.SourceColumn != String.Empty)
                        {
                            // Map to the row or to the backup hash according to state
                            param.Value = _row.RowState == DataRowState.Deleted || _row.RowState == DataRowState.Detached ?
                                          _backupFieldValues[param.SourceColumn] :
                                          _row[param.SourceColumn];
                        }
                        else
                        {
                            param.Value = DBNull.Value;
                        }
                    }

                    try
                    {
                        key = DataManager.Current.OpenConnection(this);
                        SqlDataReader reader = SelectCommand.ExecuteReader();
                        using (reader)
                        {
                            if (!reader.HasRows)
                            {
                                throw new DataItemInconsistencyException("Error retrieving item data - item no longer exists");
                            }
                            else
                            {
                                // Read the first row
                                reader.Read();

                                DataRow rowToUse;

                                // When object is part of the collection, initialize the internal table to hold retrieved fields
                                if (_owner != null && _table == null)
                                {
                                    _table = new DataTable("_table");
                                    OnInitializeTable();
                                    _retrievedFieldRow = _table.NewRow();
                                    _table.Rows.Add(_retrievedFieldRow);

                                    rowToUse = _retrievedFieldRow;
                                }
                                else
                                {
                                    rowToUse = _row;
                                }

                                foreach (string missingField in _missingFields)
                                {
                                    try
                                    {
                                        if ((_row.RowState == DataRowState.Deleted || _row.RowState == DataRowState.Detached) &&
                                            rowToUse == _row)
                                        {
                                            // Update the backup hash when state is inaccessible
                                            _backupFieldValues[missingField] = reader[missingField];
                                        }
                                        else
                                        {
                                            rowToUse[missingField] = reader[missingField];
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        // If a field assigment failed, the select command
                                        throw new DataItemInconsistencyException("Error setting item data - retrieved data does not match item's internal structure", ex);
                                    }
                                }

                                // Done getting the missing fields, so clear them
                                _missingFields.Clear();
                            }
                        }
                    }
                    finally
                    {
                        DataManager.Current.CloseConnection(key);
                    }
                }
                if (ProxyServer.InProgress)
                {
                    ProxyServer.Current.Result[ProxyServer.Current.CurrentAction].AddData("_table", _table);
                }
            }
        }