using System; namespace System.Data { /// /// Summary description for MergeManager. /// internal class MergeManager { internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction) { if(targetSet == null) throw new ArgumentNullException("targetSet"); if(sourceSet == null) throw new ArgumentNullException("sourceSet"); foreach (DataTable t in sourceSet.Tables) MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction); } internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction) { if(targetSet == null) throw new ArgumentNullException("targetSet"); if(sourceTable == null) throw new ArgumentNullException("sourceTable"); if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction)) return; DataTable targetTable = targetSet.Tables[sourceTable.TableName]; if (targetTable != null) { checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same fillData(targetTable, sourceTable, preserveChanges); } } internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction) { if(targetSet == null) throw new ArgumentNullException("targetSet"); if(sourceRows == null) throw new ArgumentNullException("sourceRows"); for (int i = 0; i < sourceRows.Length; i++) { DataRow row = sourceRows[i]; DataTable sourceTable = row.Table; if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction)) return; DataTable targetTable = targetSet.Tables[row.Table.TableName]; if (targetTable != null) { checkColumnTypes(targetTable, row.Table); MergeRow(targetTable, row, preserveChanges); } } } // merge a row into a target table. private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges) { DataColumnCollection columns = row.Table.Columns; DataColumn[] primaryKeys = targetTable.PrimaryKey; DataRow targetRow = null; DataRowVersion version = DataRowVersion.Default; if (row.RowState == DataRowState.Deleted) version = DataRowVersion.Original; if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key. { // initiate an array that has the values of the primary keys. object[] keyValues = new object[primaryKeys.Length]; for (int j = 0; j < keyValues.Length; j++) { keyValues[j] = row[primaryKeys[j].ColumnName, version]; } // find the row in the target table. targetRow = targetTable.Rows.Find(keyValues); } // row doesn't exist in target table, or there are no primary keys. // create new row and copy values from source row to the new row. if (targetRow == null) { targetRow = targetTable.NewRow(); row.CopyValuesToRow(targetRow); targetTable.Rows.Add(targetRow); } // row exists in target table, and presere changes is false - // change the values of the target row to the values of the source row. else if (!preserveChanges) { row.CopyValuesToRow(targetRow); } } // adjust the table schema according to the missingschemaaction param. // return false if adjusting fails. private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction) { string tableName = sourceTable.TableName; // if the source table not exists in the target dataset // we act according to the missingschemaaction param. int tmp = targetSet.Tables.IndexOf(tableName); // we need to check if it is equals names if (tmp != -1 && !targetSet.Tables[tmp].TableName.Equals(tableName)) tmp = -1; if (tmp == -1) { if (missingSchemaAction == MissingSchemaAction.Ignore) return true; if (missingSchemaAction == MissingSchemaAction.Error) throw new ArgumentException("Target DataSet missing definition for "+ tableName + "."); targetSet.Tables.Add((DataTable)sourceTable.Clone()); } DataTable table = targetSet.Tables[tableName]; if (!CheckPrimaryKeys(table, sourceTable)) return false; for (int i = 0; i < sourceTable.Columns.Count; i++) { DataColumn col = sourceTable.Columns[i]; // if a column from the source table doesn't exists in the target table // we act according to the missingschemaaction param. if(!table.Columns.Contains(col.ColumnName)) { if (missingSchemaAction == MissingSchemaAction.Ignore) continue; if (missingSchemaAction == MissingSchemaAction.Error) throw new ArgumentException(("Column '" + col.ColumnName + "' does not belong to table Items.")); table.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping)); } } return true; } // find if there is a valid matching between the targetTable PrimaryKey and the // sourceTable primatyKey. // return true if there is a match, else return false and raise a MergeFailedEvent. private static bool CheckPrimaryKeys(DataTable targetTable, DataTable sourceTable) { // if the length of one of the tables primarykey if 0 - there is nothing to check. if (targetTable.PrimaryKey.Length != 0 && sourceTable.PrimaryKey.Length != 0) { // if the length of primarykey is not equal - merge fails if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) { string message = ".PrimaryKey and .PrimaryKey have different Length."; MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message); targetTable.DataSet.OnMergeFailed(e); return false; } else { // we have to see that each primary column in the target table // has a column with the same name in the sourcetable primarykey columns. bool foundMatch; DataColumn[] targetDataColumns = targetTable.PrimaryKey; DataColumn[] srcDataColumns = sourceTable.PrimaryKey; // loop on all primary key columns in the targetTable. for (int i = 0; i < targetDataColumns.Length; i++) { foundMatch = false; DataColumn col = targetDataColumns[i]; // find if there is a column with the same name in the // sourceTable primary key columns. for (int j = 0; j < srcDataColumns.Length; j++) { if (srcDataColumns[j].ColumnName == col.ColumnName) { foundMatch = true; break; } } if (!foundMatch) { string message = "Mismatch columns in the PrimaryKey : ." + col.ColumnName + " versus ." + srcDataColumns[i].ColumnName + "."; MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message); targetTable.DataSet.OnMergeFailed(e); return false; } } } } return true; } // fill the data from the source table to the target table private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges) { for (int i = 0; i < sourceTable.Rows.Count; i++) { DataRow row = sourceTable.Rows[i]; MergeRow(targetTable, row, preserveChanges); } } // check tha column from 2 tables that has the same name also has the same datatype. private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable) { for (int i = 0; i < sourceTable.Columns.Count; i++) { DataColumn fromCol = sourceTable.Columns[i]; DataColumn toCol = targetTable.Columns[fromCol.ColumnName]; if((toCol != null) && (toCol.DataType != fromCol.DataType)) throw new DataException("." + fromCol.ColumnName + " and ." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch."); } } } }