/*=========================*/ #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); } } }