throw new ArgumentNullException("targetSet");
if(sourceSet == null)
throw new ArgumentNullException("sourceSet");
+ if (sourceSet == targetSet)
+ return;
+
+ bool prevEC = targetSet.EnforceConstraints;
+ targetSet.EnforceConstraints = false;
foreach (DataTable t in sourceSet.Tables)
MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
- AdjustSchema(targetSet,sourceSet,missingSchemaAction);
-
+ AdjustSchemaRelations (targetSet, sourceSet, missingSchemaAction);
+ targetSet.EnforceConstraints = prevEC;
}
internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
throw new ArgumentNullException("targetSet");
if(sourceTable == null)
throw new ArgumentNullException("sourceTable");
+ if (sourceTable.DataSet == targetSet)
+ return;
bool savedEnfoceConstraints = targetSet.EnforceConstraints;
targetSet.EnforceConstraints = false;
DataTable targetTable = null;
- if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
+ if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
return;
- }
- if (targetTable != null) {
- checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same
+ if (targetTable != null)
fillData(targetTable, sourceTable, preserveChanges);
- }
targetSet.EnforceConstraints = savedEnfoceConstraints;
-
- if (!targetSet.EnforceConstraints && targetTable != null) {
- // indexes are still outdated
- targetTable.ResetIndexes();
- }
+ }
+
+ internal static void Merge (DataTable targetTable,
+ DataTable sourceTable,
+ bool preserveChanges,
+ MissingSchemaAction missingSchemaAction)
+ {
+ if(targetTable== null)
+ throw new ArgumentNullException("targetTable");
+ if(sourceTable == null)
+ throw new ArgumentNullException("sourceTable");
+ if (sourceTable == targetTable)
+ return;
+
+ bool savedEnforceConstraints = targetTable.EnforceConstraints;
+ targetTable.EnforceConstraints = false;
+
+ if (!AdjustSchema(targetTable, sourceTable, missingSchemaAction))
+ return;
+
+ fillData(targetTable, sourceTable, preserveChanges);
+ targetTable.EnforceConstraints = savedEnforceConstraints;
}
internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
DataRow row = sourceRows[i];
DataTable sourceTable = row.Table;
DataTable targetTable = null;
- if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
+ if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
return;
- }
if (targetTable != null) {
- checkColumnTypes(targetTable, row.Table);
MergeRow(targetTable, row, preserveChanges);
- if (!(targetTables.IndexOf(targetTable) >= 0)) {
+ if (!(targetTables.IndexOf(targetTable) >= 0))
targetTables.Add(targetTable);
- }
}
}
targetSet.EnforceConstraints = savedEnfoceConstraints;
-
- foreach(DataTable table in targetTables) {
- table.ResetIndexes();
- }
}
// 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;
}
// find the row in the target table.
- targetRow = targetTable.Rows.Find(keyValues);
+ targetRow = targetTable.Rows.Find(keyValues, DataViewRowState.OriginalRows);
+ if (targetRow == null)
+ 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();
- targetRow.Proposed = -1;
- row.CopyValuesToRow(targetRow);
- targetTable.Rows.Add(targetRow);
+ DataRow newRow = targetTable.NewNotInitializedRow();
+ // Don't check for ReadOnly, when cloning data to new uninitialized row.
+ row.CopyValuesToRow(newRow, false);
+ targetTable.Rows.AddInternal (newRow);
}
// 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)
+ else
{
- row.CopyValuesToRow(targetRow);
+ row.MergeValuesToRow(targetRow, preserveChanges);
}
}
-
- // adjust the dataset schema according to the missingschemaaction param
- // (relations).
- // return false if adjusting fails.
- private static bool AdjustSchema(DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
+ private static bool AdjustSchemaRelations (DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
{
- if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey) {
- foreach (DataRelation relation in sourceSet.Relations) {
- // TODO : add more precise condition (columns)
- if (!targetSet.Relations.Contains(relation.RelationName)) {
- DataTable targetTable = targetSet.Tables[relation.ParentColumns[0].Table.TableName];
- DataColumn[] parentColumns = ResolveColumns(sourceSet,targetTable,relation.ParentColumns);
- targetTable = targetSet.Tables[relation.ChildColumns[0].Table.TableName];
- DataColumn[] childColumns = ResolveColumns(sourceSet,targetTable,relation.ChildColumns);
- if (parentColumns != null && childColumns != null) {
- DataRelation newRelation = new DataRelation(relation.RelationName,parentColumns,childColumns);
- newRelation.Nested = relation.Nested;
- targetSet.Relations.Add(newRelation);
- }
+ if (missingSchemaAction == MissingSchemaAction.Ignore)
+ return true;
+
+ foreach(DataTable sourceTable in sourceSet.Tables) {
+
+ DataTable targetTable = targetSet.Tables[sourceTable.TableName];
+ if (targetTable == null)
+ continue;
+
+ foreach (Constraint constraint in sourceTable.Constraints) {
+
+ Constraint targetConstraint = null;
+
+ string constraintName = constraint.ConstraintName;
+ if (targetTable.Constraints.Contains (constraintName))
+ constraintName = "";
+
+ UniqueConstraint uc = constraint as UniqueConstraint;
+ // PrimaryKey is already taken care of while merging the table
+ // ForeignKey constraint takes care of Parent Unique Constraints
+ if (uc != null) {
+ if (uc.IsPrimaryKey || uc.ChildConstraint != null)
+ continue;
+ DataColumn[] columns = ResolveColumns (targetTable, uc.Columns);
+ targetConstraint = new UniqueConstraint (constraintName, columns, false);
}
- else {
- // TODO : should we throw an exeption ?
+
+ ForeignKeyConstraint fc = constraint as ForeignKeyConstraint;
+ if (fc != null) {
+ DataColumn[] columns = ResolveColumns (targetTable, fc.Columns);
+ DataColumn[] relatedColumns = ResolveColumns (targetSet.Tables [fc.RelatedTable.TableName],
+ fc.RelatedColumns);
+ targetConstraint = new ForeignKeyConstraint (constraintName, relatedColumns, columns);
}
- }
-
- foreach(DataTable sourceTable in sourceSet.Tables) {
- DataTable targetTable = targetSet.Tables[sourceTable.TableName];
-
- if (targetTable != null) {
- foreach(Constraint constraint in sourceTable.Constraints) {
-
- if (constraint is UniqueConstraint) {
- UniqueConstraint uc = (UniqueConstraint)constraint;
- // FIXME : add more precise condition (columns)
- if ( !targetTable.Constraints.Contains(uc.ConstraintName) ) {
- DataColumn[] columns = ResolveColumns(sourceSet,targetTable,uc.Columns);
- if (columns != null) {
- UniqueConstraint newConstraint = new UniqueConstraint(uc.ConstraintName,columns,uc.IsPrimaryKey);
- targetTable.Constraints.Add(newConstraint);
- }
- }
- else {
- // FIXME : should we throw an exception ?
- }
- }
- else {
- ForeignKeyConstraint fc = (ForeignKeyConstraint)constraint;
- // FIXME : add more precise condition (columns)
- if (!targetTable.Constraints.Contains(fc.ConstraintName)) {
- DataColumn[] columns = ResolveColumns(sourceSet,targetTable,fc.Columns);
- DataTable relatedTable = targetSet.Tables[fc.RelatedTable.TableName];
- DataColumn[] relatedColumns = ResolveColumns(sourceSet,relatedTable,fc.RelatedColumns);
- if (columns != null && relatedColumns != null) {
- ForeignKeyConstraint newConstraint = new ForeignKeyConstraint(fc.ConstraintName,relatedColumns,columns);
- targetTable.Constraints.Add(newConstraint);
- }
- }
- else {
- // FIXME : should we throw an exception ?
- }
- }
- }
+
+ bool dupConstraintFound = false;
+ foreach (Constraint cons in targetTable.Constraints) {
+ if (!targetConstraint.Equals (cons))
+ continue;
+ dupConstraintFound = true;
+ break;
}
+
+ // If equivalent-constraint already exists, then just do nothing
+ if (dupConstraintFound)
+ continue;
+
+ if (missingSchemaAction == MissingSchemaAction.Error)
+ throw new DataException ("Target DataSet missing " + targetConstraint.GetType () +
+ targetConstraint.ConstraintName);
+ else
+ targetTable.Constraints.Add (targetConstraint);
+ }
+ }
+
+ foreach (DataRelation relation in sourceSet.Relations) {
+ DataRelation targetRelation = targetSet.Relations [relation.RelationName];
+ if (targetRelation == null) {
+ if (missingSchemaAction == MissingSchemaAction.Error)
+ throw new ArgumentException ("Target DataSet mising definition for " +
+ relation.RelationName);
+
+ DataColumn[] parentColumns = ResolveColumns (targetSet.Tables [relation.ParentTable.TableName],
+ relation.ParentColumns);
+ DataColumn[] childColumns = ResolveColumns (targetSet.Tables [relation.ChildTable.TableName],
+ relation.ChildColumns);
+ targetRelation = targetSet.Relations.Add (relation.RelationName, parentColumns,
+ childColumns, relation.createConstraints);
+ targetRelation.Nested = relation.Nested;
+ } else if (!CompareColumnArrays (relation.ParentColumns, targetRelation.ParentColumns) ||
+ !CompareColumnArrays (relation.ChildColumns, targetRelation.ChildColumns)) {
+ RaiseMergeFailedEvent (null, "Relation " + relation.RelationName +
+ " cannot be merged, because keys have mismatch columns.");
}
}
return true;
}
- private static DataColumn[] ResolveColumns(DataSet targetSet,DataTable targetTable,DataColumn[] sourceColumns)
+ private static DataColumn[] ResolveColumns(DataTable targetTable, DataColumn[] sourceColumns)
{
if (sourceColumns != null && sourceColumns.Length > 0) {
- // TODO : worth to check that all source colums come from the same table
+ // lets just assume that all columns are from the Same table
if (targetTable != null) {
int i=0;
DataColumn[] targetColumns = new DataColumn[sourceColumns.Length];
+
+ DataColumn tmpCol ;
foreach(DataColumn sourceColumn in sourceColumns) {
- targetColumns[i++] = targetTable.Columns[sourceColumn.ColumnName];
+ tmpCol = targetTable.Columns[sourceColumn.ColumnName];
+ if (tmpCol == null)
+ throw new DataException ("Column " + sourceColumn.ColumnName +
+ " does not belong to table " + targetTable.TableName);
+ targetColumns [i++] = tmpCol;
}
return targetColumns;
}
return null;
}
-
+
// adjust the table schema according to the missingschemaaction param.
// return false if adjusting fails.
private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction, ref DataTable newTable)
{
- 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) {
+ DataTable targetTable = targetSet.Tables [sourceTable.TableName];
+ if (targetTable == null) {
+ if (missingSchemaAction == MissingSchemaAction.Ignore)
return true;
- }
- if (missingSchemaAction == MissingSchemaAction.Error) {
- throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");
- }
-
- DataTable cloneTable = (DataTable)sourceTable.Clone();
- targetSet.Tables.Add(cloneTable);
- tableName = cloneTable.TableName;
- }
-
- DataTable table = targetSet.Tables[tableName];
-
+
+ if (missingSchemaAction == MissingSchemaAction.Error)
+ throw new ArgumentException ("Target DataSet missing definition for " +
+ sourceTable.TableName + ".");
+
+ targetTable = (DataTable)sourceTable.Clone();
+ targetSet.Tables.Add(targetTable);
+ }
+
+ AdjustSchema (targetTable, sourceTable, missingSchemaAction);
+
+ newTable = targetTable;
+ return true;
+ }
+
+
+ private static bool AdjustSchema(DataTable targetTable, DataTable sourceTable, MissingSchemaAction missingSchemaAction)
+ {
+ if (missingSchemaAction == MissingSchemaAction.Ignore)
+ return true;
+
for (int i = 0; i < sourceTable.Columns.Count; i++) {
DataColumn sourceColumn = sourceTable.Columns[i];
// if a column from the source table doesn't exists in the target table
// we act according to the missingschemaaction param.
- DataColumn targetColumn = table.Columns[sourceColumn.ColumnName];
+ DataColumn targetColumn = targetTable.Columns [sourceColumn.ColumnName];
if(targetColumn == null) {
- if (missingSchemaAction == MissingSchemaAction.Ignore) {
- continue;
- }
- if (missingSchemaAction == MissingSchemaAction.Error) {
- throw new ArgumentException(("Column '" + sourceColumn.ColumnName + "' does not belong to table Items."));
- }
+ if (missingSchemaAction == MissingSchemaAction.Error)
+ throw new DataException ("Target table " + targetTable.TableName +
+ " missing definition for column " + sourceColumn.ColumnName);
- targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType, sourceColumn.Expression, sourceColumn.ColumnMapping);
- table.Columns.Add(targetColumn);
- }
-
- if (sourceColumn.Unique) {
- try {
- targetColumn.Unique = sourceColumn.Unique;
- }
- catch(Exception e){
-// Console.WriteLine("targetColumn : {0} targetTable : {1} ",targetColumn.ColumnName,table.TableName);
- foreach(DataRow row in table.Rows) {
-// Console.WriteLine(row[targetColumn]);
- }
- throw e;
- }
+ targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType,
+ sourceColumn.Expression, sourceColumn.ColumnMapping);
+ targetTable.Columns.Add(targetColumn);
}
if(sourceColumn.AutoIncrement) {
}
}
- if (!AdjustPrimaryKeys(table, sourceTable)) {
+ if (!AdjustPrimaryKeys(targetTable, sourceTable))
return false;
- }
- newTable = table;
+ checkColumnTypes (targetTable, sourceTable);
+
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 AdjustPrimaryKeys(DataTable targetTable, DataTable sourceTable)
{
- // if the length of one of the tables primarykey if 0 - there is nothing to check.
- if (sourceTable.PrimaryKey.Length != 0) {
- if (targetTable.PrimaryKey.Length == 0) {
- // if target table has no primary key at all -
- // import primary key from source table
- DataColumn[] targetColumns = new DataColumn[sourceTable.PrimaryKey.Length];
-
- for(int i=0; i < sourceTable.PrimaryKey.Length; i++){
- DataColumn sourceColumn = sourceTable.PrimaryKey[i];
- DataColumn targetColumn = targetTable.Columns[sourceColumn.ColumnName];
-
- if (targetColumn == null) {
- // is target table has no column corresponding
- // to source table PK column - merge fails
- string message = "Column " + sourceColumn.ColumnName + " does not belongs to table " + targetTable.TableName;
- MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
- targetTable.DataSet.OnMergeFailed(e);
- return false;
- }
- else {
- targetColumns[i] = targetColumn;
- }
- }
- targetTable.PrimaryKey = targetColumns;
- }
- else {
- // if target table already has primary key and
- // if the length of primarykey is not equal - merge fails
- if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
- string message = "<target>.PrimaryKey and <source>.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 : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";
- MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
- targetTable.DataSet.OnMergeFailed(e);
- return false;
- }
-
- }
- }
- }
+ if (sourceTable.PrimaryKey.Length == 0)
+ return true;
+
+ // If targetTable does not have a PrimaryKey, just import the sourceTable PrimaryKey
+ if (targetTable.PrimaryKey.Length == 0) {
+ DataColumn[] targetColumns = ResolveColumns (targetTable, sourceTable.PrimaryKey);
+ targetTable.PrimaryKey = targetColumns;
+ return true;
+ }
+
+ // If both the tables have a primary key, verify that they are equivalent.
+ // raise a MergeFailedEvent if the keys are not equivalent
+ if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
+ RaiseMergeFailedEvent (targetTable, "<target>.PrimaryKey and <source>.PrimaryKey have different Length.");
+ return false;
}
+ for (int i=0; i < targetTable.PrimaryKey.Length; ++i) {
+ if (targetTable.PrimaryKey [i].ColumnName.Equals (sourceTable.PrimaryKey [i].ColumnName))
+ continue;
+ RaiseMergeFailedEvent (targetTable, "Mismatch columns in the PrimaryKey : <target>." +
+ targetTable.PrimaryKey [i].ColumnName + " versus <source>." + sourceTable.PrimaryKey [i].ColumnName);
+ 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)
{
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)
{
{
DataColumn fromCol = sourceTable.Columns[i];
DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
- if((toCol != null) && (toCol.DataType != fromCol.DataType))
- throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");
+ if (toCol == null)
+ continue;
+ if (toCol.DataTypeMatches (fromCol))
+ continue;
+ throw new DataException("<target>." + fromCol.ColumnName + " and <source>." +
+ fromCol.ColumnName + " have conflicting properties: DataType " +
+ " property mismatch.");
}
}
+
+ private static bool CompareColumnArrays (DataColumn[] arr1, DataColumn[] arr2)
+ {
+ if (arr1.Length != arr2.Length)
+ return false;
+
+ for (int i=0; i < arr1.Length; ++i)
+ if (!arr1 [i].ColumnName.Equals (arr2 [i].ColumnName))
+ return false;
+ return true;
+ }
+
+ private static void RaiseMergeFailedEvent (DataTable targetTable, string errMsg)
+ {
+ MergeFailedEventArgs args = new MergeFailedEventArgs (targetTable, errMsg);
+ if (targetTable.DataSet != null)
+ targetTable.DataSet.OnMergeFailed (args);
+ }
}
}