6 /// Summary description for MergeManager.
8 internal class MergeManager
10 internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
13 throw new ArgumentNullException("targetSet");
15 throw new ArgumentNullException("sourceSet");
17 foreach (DataTable t in sourceSet.Tables)
18 MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
20 AdjustSchema(targetSet,sourceSet,missingSchemaAction);
24 internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
27 throw new ArgumentNullException("targetSet");
28 if(sourceTable == null)
29 throw new ArgumentNullException("sourceTable");
31 DataTable targetTable = null;
32 if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
35 if (targetTable != null) {
36 checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same
37 fillData(targetTable, sourceTable, preserveChanges);
41 internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
44 throw new ArgumentNullException("targetSet");
45 if(sourceRows == null)
46 throw new ArgumentNullException("sourceRows");
48 for (int i = 0; i < sourceRows.Length; i++) {
49 DataRow row = sourceRows[i];
50 DataTable sourceTable = row.Table;
51 DataTable targetTable = null;
52 if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
55 if (targetTable != null) {
56 checkColumnTypes(targetTable, row.Table);
57 MergeRow(targetTable, row, preserveChanges);
62 // merge a row into a target table.
63 private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)
65 DataColumnCollection columns = row.Table.Columns;
66 DataColumn[] primaryKeys = targetTable.PrimaryKey;
67 DataRow targetRow = null;
68 DataRowVersion version = DataRowVersion.Default;
69 if (row.RowState == DataRowState.Deleted)
70 version = DataRowVersion.Original;
72 if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.
74 // initiate an array that has the values of the primary keys.
75 object[] keyValues = new object[primaryKeys.Length];
77 for (int j = 0; j < keyValues.Length; j++)
79 keyValues[j] = row[primaryKeys[j].ColumnName, version];
82 // find the row in the target table.
83 targetRow = targetTable.Rows.Find(keyValues);
85 // row doesn't exist in target table, or there are no primary keys.
86 // create new row and copy values from source row to the new row.
87 if (targetRow == null)
89 targetRow = targetTable.NewRow();
90 row.CopyValuesToRow(targetRow);
91 targetTable.Rows.Add(targetRow);
93 // row exists in target table, and presere changes is false -
94 // change the values of the target row to the values of the source row.
95 else if (!preserveChanges)
97 row.CopyValuesToRow(targetRow);
103 // adjust the dataset schema according to the missingschemaaction param
105 // return false if adjusting fails.
106 private static bool AdjustSchema(DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
108 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey) {
109 foreach (DataRelation relation in sourceSet.Relations) {
110 // TODO : add more precise condition (columns)
111 if (!targetSet.Relations.Contains(relation.RelationName)) {
112 DataTable targetTable = targetSet.Tables[relation.ParentColumns[0].Table.TableName];
113 DataColumn[] parentColumns = ResolveColumns(sourceSet,targetTable,relation.ParentColumns);
114 targetTable = targetSet.Tables[relation.ChildColumns[0].Table.TableName];
115 DataColumn[] childColumns = ResolveColumns(sourceSet,targetTable,relation.ChildColumns);
116 if (parentColumns != null && childColumns != null) {
117 DataRelation newRelation = new DataRelation(relation.RelationName,parentColumns,childColumns);
118 targetSet.Relations.Add(newRelation);
122 // TODO : should we throw an exeption ?
126 foreach(DataTable sourceTable in sourceSet.Tables) {
127 DataTable targetTable = targetSet.Tables[sourceTable.TableName];
129 if (targetTable != null) {
130 foreach(UniqueConstraint uc in sourceTable.Constraints.UniqueConstraints) {
131 // TODO : add more precise condition (columns)
132 if ( !targetTable.Constraints.Contains(uc.ConstraintName) ) {
133 DataColumn[] columns = ResolveColumns(sourceSet,targetTable,uc.Columns);
134 if (columns != null) {
135 UniqueConstraint newConstraint = new UniqueConstraint(uc.ConstraintName,columns,uc.IsPrimaryKey);
136 targetTable.Constraints.Add(newConstraint);
140 // TODO : should we throw an exeption ?
144 foreach(ForeignKeyConstraint fc in sourceTable.Constraints.ForeignKeyConstraints) {
145 // TODO : add more precise condition (columns)
146 if (!targetTable.Constraints.Contains(fc.ConstraintName)) {
147 DataColumn[] columns = ResolveColumns(sourceSet,targetTable,fc.Columns);
148 DataTable relatedTable = targetSet.Tables[fc.RelatedTable.TableName];
149 DataColumn[] relatedColumns = ResolveColumns(sourceSet,relatedTable,fc.RelatedColumns);
150 if (columns != null && relatedColumns != null) {
151 ForeignKeyConstraint newConstraint = new ForeignKeyConstraint(fc.ConstraintName,relatedColumns,columns);
152 targetTable.Constraints.Add(newConstraint);
156 // TODO : should we throw an exeption ?
166 private static DataColumn[] ResolveColumns(DataSet targetSet,DataTable targetTable,DataColumn[] sourceColumns)
168 if (sourceColumns != null && sourceColumns.Length > 0) {
169 // TODO : worth to check that all source colums come from the same table
170 if (targetTable != null) {
172 DataColumn[] targetColumns = new DataColumn[sourceColumns.Length];
173 foreach(DataColumn sourceColumn in sourceColumns) {
174 targetColumns[i++] = targetTable.Columns[sourceColumn.ColumnName];
176 return targetColumns;
183 // adjust the table schema according to the missingschemaaction param.
184 // return false if adjusting fails.
185 private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction, ref DataTable newTable)
187 string tableName = sourceTable.TableName;
189 // if the source table not exists in the target dataset
190 // we act according to the missingschemaaction param.
191 int tmp = targetSet.Tables.IndexOf(tableName);
192 // we need to check if it is equals names
193 if (tmp != -1 && !targetSet.Tables[tmp].TableName.Equals(tableName))
196 if (missingSchemaAction == MissingSchemaAction.Ignore) {
199 if (missingSchemaAction == MissingSchemaAction.Error) {
200 throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");
203 DataTable cloneTable = (DataTable)sourceTable.Clone();
204 targetSet.Tables.Add(cloneTable);
205 tableName = cloneTable.TableName;
208 DataTable table = targetSet.Tables[tableName];
210 for (int i = 0; i < sourceTable.Columns.Count; i++) {
211 DataColumn sourceColumn = sourceTable.Columns[i];
212 // if a column from the source table doesn't exists in the target table
213 // we act according to the missingschemaaction param.
214 DataColumn targetColumn = table.Columns[sourceColumn.ColumnName];
215 if(targetColumn == null) {
216 if (missingSchemaAction == MissingSchemaAction.Ignore) {
219 if (missingSchemaAction == MissingSchemaAction.Error) {
220 throw new ArgumentException(("Column '" + sourceColumn.ColumnName + "' does not belong to table Items."));
223 targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType, sourceColumn.Expression, sourceColumn.ColumnMapping);
224 table.Columns.Add(targetColumn);
227 if (sourceColumn.Unique) {
229 targetColumn.Unique = sourceColumn.Unique;
232 Console.WriteLine("targetColumn : {0} targetTable : {1} ",targetColumn.ColumnName,table.TableName);
233 foreach(DataRow row in table.Rows) {
234 Console.WriteLine(row[targetColumn]);
240 if(sourceColumn.AutoIncrement) {
241 targetColumn.AutoIncrement = sourceColumn.AutoIncrement;
242 targetColumn.AutoIncrementSeed = sourceColumn.AutoIncrementSeed;
243 targetColumn.AutoIncrementStep = sourceColumn.AutoIncrementStep;
247 if (!AdjustPrimaryKeys(table, sourceTable)) {
255 // find if there is a valid matching between the targetTable PrimaryKey and the
256 // sourceTable primatyKey.
257 // return true if there is a match, else return false and raise a MergeFailedEvent.
258 private static bool AdjustPrimaryKeys(DataTable targetTable, DataTable sourceTable)
260 // if the length of one of the tables primarykey if 0 - there is nothing to check.
261 if (sourceTable.PrimaryKey.Length != 0) {
262 if (targetTable.PrimaryKey.Length == 0) {
263 // if target table has no primary key at all -
264 // import primary key from source table
265 DataColumn[] targetColumns = new DataColumn[sourceTable.PrimaryKey.Length];
267 for(int i=0; i < sourceTable.PrimaryKey.Length; i++){
268 DataColumn sourceColumn = sourceTable.PrimaryKey[i];
269 DataColumn targetColumn = targetTable.Columns[sourceColumn.ColumnName];
271 if (targetColumn == null) {
272 // is target table has no column corresponding
273 // to source table PK column - merge fails
274 string message = "Column " + sourceColumn.ColumnName + " does not belongs to table " + targetTable.TableName;
275 MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
276 targetTable.DataSet.OnMergeFailed(e);
280 targetColumns[i] = targetColumn;
283 targetTable.PrimaryKey = targetColumns;
286 // if target table already has primary key and
287 // if the length of primarykey is not equal - merge fails
288 if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
289 string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";
290 MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
291 targetTable.DataSet.OnMergeFailed(e);
295 // we have to see that each primary column in the target table
296 // has a column with the same name in the sourcetable primarykey columns.
298 DataColumn[] targetDataColumns = targetTable.PrimaryKey;
299 DataColumn[] srcDataColumns = sourceTable.PrimaryKey;
301 // loop on all primary key columns in the targetTable.
302 for (int i = 0; i < targetDataColumns.Length; i++) {
304 DataColumn col = targetDataColumns[i];
306 // find if there is a column with the same name in the
307 // sourceTable primary key columns.
308 for (int j = 0; j < srcDataColumns.Length; j++) {
309 if (srcDataColumns[j].ColumnName == col.ColumnName) {
315 string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";
316 MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
317 targetTable.DataSet.OnMergeFailed(e);
329 // fill the data from the source table to the target table
330 private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)
332 for (int i = 0; i < sourceTable.Rows.Count; i++)
334 DataRow row = sourceTable.Rows[i];
335 MergeRow(targetTable, row, preserveChanges);
339 // check tha column from 2 tables that has the same name also has the same datatype.
340 private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)
342 for (int i = 0; i < sourceTable.Columns.Count; i++)
344 DataColumn fromCol = sourceTable.Columns[i];
345 DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
346 if((toCol != null) && (toCol.DataType != fromCol.DataType))
347 throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");