| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- //------------------------------------------------------------------------------
- // <copyright file="Merger.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- /// <devdoc>
- /// Merge Utilities.
- /// </devdoc>
- internal sealed class Merger {
- private DataSet dataSet = null;
- private DataTable dataTable = null;
- private bool preserveChanges;
- private MissingSchemaAction missingSchemaAction;
- private bool isStandAlonetable = false;
- private bool _IgnoreNSforTableLookup = false; // Everett Behavior : SQL BU DT 370850
- internal Merger(DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction) {
- this.dataSet = dataSet;
- this.preserveChanges = preserveChanges;
- // map AddWithKey -> Add
- if (missingSchemaAction == MissingSchemaAction.AddWithKey)
- this.missingSchemaAction = MissingSchemaAction.Add;
- else
- this.missingSchemaAction = missingSchemaAction;
- }
- internal Merger(DataTable dataTable, bool preserveChanges, MissingSchemaAction missingSchemaAction) {
- isStandAlonetable = true;
- this.dataTable = dataTable;
- this.preserveChanges = preserveChanges;
- // map AddWithKey -> Add
- if (missingSchemaAction == MissingSchemaAction.AddWithKey)
- this.missingSchemaAction = MissingSchemaAction.Add;
- else
- this.missingSchemaAction = missingSchemaAction;
- }
- internal void MergeDataSet(DataSet source) {
- if (source == dataSet) return; //somebody is doing an 'automerge'
- bool fEnforce = dataSet.EnforceConstraints;
- dataSet.EnforceConstraints = false;
- _IgnoreNSforTableLookup = (dataSet.namespaceURI != source.namespaceURI); // if two DataSets have different
- // Namespaces, ignore NS for table lookups as we wont be able to find the right tables which inherits its NS
- List<DataColumn> existingColumns = null;// need to cache existing columns
- if (MissingSchemaAction.Add == missingSchemaAction) {
- existingColumns = new List<DataColumn>(); // need to cache existing columns
- foreach(DataTable dt in dataSet.Tables) {
- foreach(DataColumn dc in dt.Columns) {
- existingColumns.Add(dc);
- }
- }
- }
- for (int i = 0; i < source.Tables.Count; i++) {
- MergeTableData(source.Tables[i]); // since column expression might have dependency on relation, we do not set
- //column expression at this point. We need to set it after adding relations
- }
- if (MissingSchemaAction.Ignore != missingSchemaAction) {
- // Add all independent constraints
- MergeConstraints(source);
- // Add all relationships
- for (int i = 0; i < source.Relations.Count; i++) {
- MergeRelation(source.Relations[i]);
- }
- }
- // WebData 88234
- if (MissingSchemaAction.Add == missingSchemaAction) { // for which other options we should add expressions also?
- foreach (DataTable sourceTable in source.Tables) {
- DataTable targetTable;
- if (_IgnoreNSforTableLookup) {
- targetTable = dataSet.Tables[sourceTable.TableName];
- }
- else {
- targetTable = dataSet.Tables[sourceTable.TableName, sourceTable.Namespace];// we know that target table wont be null since MissingSchemaAction is Add , we have already added it!
- }
-
- foreach(DataColumn dc in sourceTable.Columns) { // Should we overwrite the previous expression column? No, refer to spec, if it is new column we need to add the schema
- if (dc.Computed) {
- DataColumn targetColumn = targetTable.Columns[dc.ColumnName];
- if (!existingColumns.Contains(targetColumn)) {
- targetColumn.Expression = dc.Expression;
- }
- }
- }
- }
- }
- MergeExtendedProperties(source.ExtendedProperties, dataSet.ExtendedProperties);
- foreach(DataTable dt in dataSet.Tables)
- dt.EvaluateExpressions();
- dataSet.EnforceConstraints = fEnforce;
- }
- internal void MergeTable(DataTable src) {
- bool fEnforce = false;
- if (!isStandAlonetable) {
- if (src.DataSet == dataSet) return; //somebody is doing an 'automerge'
- fEnforce = dataSet.EnforceConstraints;
- dataSet.EnforceConstraints = false;
- }
- else {
- if (src == dataTable) return; //somebody is doing an 'automerge'
- dataTable.SuspendEnforceConstraints = true;
- }
- if (this.dataSet != null) { // this is ds.Merge
- // if source does not have a DS, or if NS of both DS does not match, ignore the NS
- if (src.DataSet == null || src.DataSet.namespaceURI != this.dataSet.namespaceURI) {
- _IgnoreNSforTableLookup = true;
- }
- }
- else { // this is dt.Merge
- if (this.dataTable.DataSet == null || src.DataSet == null ||
- src.DataSet.namespaceURI != this.dataTable.DataSet.namespaceURI) {
- _IgnoreNSforTableLookup = true;
- }
- }
- MergeTableData(src);
- DataTable dt = dataTable;
- if (dt == null && dataSet != null) {
- if (_IgnoreNSforTableLookup) {
- dt = dataSet.Tables[src.TableName];
- }
- else {
- dt = dataSet.Tables[src.TableName, src.Namespace];
- }
- }
- if (dt != null) {
- dt.EvaluateExpressions();
- }
- if (!isStandAlonetable) {
- dataSet.EnforceConstraints = fEnforce;
- }
- else {
- dataTable.SuspendEnforceConstraints = false;
- try {
- if (dataTable.EnforceConstraints) {
- dataTable.EnableConstraints();
- }
- }
- catch(ConstraintException) {
- if (dataTable.DataSet != null) {
- dataTable.DataSet.EnforceConstraints = false;
- }
- throw;
- }
- }
- }
- private void MergeTable(DataTable src, DataTable dst) {
- int rowsCount = src.Rows.Count;
- bool wasEmpty = dst.Rows.Count == 0;
- if(0 < rowsCount) {
- Index ndxSearch = null;
- DataKey key = default(DataKey);
- dst.SuspendIndexEvents();
- try {
- if(! wasEmpty && dst.primaryKey != null) {
- key = GetSrcKey(src, dst);
- if (key.HasValue)
- ndxSearch = dst.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows | DataViewRowState.Added );
- }
- // SQLBU 414992: Serious performance issue when calling Merge
- // this improves performance by iterating over the rows instead of computing their position
- foreach(DataRow sourceRow in src.Rows) {
- DataRow targetRow = null;
- if(ndxSearch != null) {
- targetRow = dst.FindMergeTarget(sourceRow, key, ndxSearch);
- }
- dst.MergeRow(sourceRow, targetRow, preserveChanges, ndxSearch);
- }
- }
- finally {
- dst.RestoreIndexEvents(true);
- }
- }
- MergeExtendedProperties(src.ExtendedProperties, dst.ExtendedProperties);
- }
- internal void MergeRows(DataRow[] rows) {
- DataTable src = null;
- DataTable dst = null;
- DataKey key = default(DataKey);
- Index ndxSearch = null;
- bool fEnforce = dataSet.EnforceConstraints;
- dataSet.EnforceConstraints = false;
- for (int i = 0; i < rows.Length; i++) {
- DataRow row = rows[i];
- if (row == null) {
- throw ExceptionBuilder.ArgumentNull("rows[" + i + "]");
- }
- if (row.Table == null) {
- throw ExceptionBuilder.ArgumentNull("rows[" + i + "].Table");
- }
- //somebody is doing an 'automerge'
- if (row.Table.DataSet == dataSet)
- continue;
- if (src != row.Table) { // row.Table changed from prev. row.
- src = row.Table;
- dst = MergeSchema(row.Table);
- if (dst == null) {
- Debug.Assert(MissingSchemaAction.Ignore == missingSchemaAction, "MergeSchema failed");
- dataSet.EnforceConstraints = fEnforce;
- return;
- }
- if(dst.primaryKey != null) {
- key = GetSrcKey(src, dst);
- }
- if (key.HasValue) {
- // Getting our own copy instead. ndxSearch = dst.primaryKey.Key.GetSortIndex();
- // IMO, Better would be to reuse index
- // ndxSearch = dst.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows | DataViewRowState.Added );
- if (null != ndxSearch) {
- ndxSearch.RemoveRef();
- ndxSearch = null;
- }
- ndxSearch = new Index(dst, dst.primaryKey.Key.GetIndexDesc(), DataViewRowState.OriginalRows | DataViewRowState.Added, (IFilter)null);
- ndxSearch.AddRef(); // need to addref twice, otherwise it will be collected
- ndxSearch.AddRef(); // in past first adref was done in const
- }
- }
- if (row.newRecord == -1 && row.oldRecord == -1)
- continue;
- DataRow targetRow = null;
- if(0 < dst.Rows.Count && ndxSearch != null) {
- targetRow = dst.FindMergeTarget(row, key, ndxSearch);
- }
- targetRow = dst.MergeRow(row, targetRow, preserveChanges, ndxSearch);
- if (targetRow.Table.dependentColumns != null && targetRow.Table.dependentColumns.Count > 0)
- targetRow.Table.EvaluateExpressions(targetRow, DataRowAction.Change, null);
- }
- if (null != ndxSearch) {
- ndxSearch.RemoveRef();
- ndxSearch = null;
- }
- dataSet.EnforceConstraints = fEnforce;
- }
- private DataTable MergeSchema(DataTable table) {
- DataTable targetTable = null;
- if (!isStandAlonetable) {
- if (dataSet.Tables.Contains(table.TableName, true))
- if (_IgnoreNSforTableLookup) {
- targetTable = dataSet.Tables[table.TableName];
- }
- else {
- targetTable = dataSet.Tables[table.TableName, table.Namespace];
- }
- }
- else {
- targetTable = dataTable;
- }
- if (targetTable == null) { // in case of standalone table, we make sure that targetTable is not null, so if this check passes, it will be when it is called via detaset
- if (MissingSchemaAction.Add == missingSchemaAction) {
- targetTable = table.Clone(table.DataSet); // if we are here mainly we are called from DataSet.Merge at this point we don't set
- //expression columns, since it might have refer to other columns via relation, so it wont find the table and we get exception;
- // do it after adding relations.
- dataSet.Tables.Add(targetTable);
- }
- else if (MissingSchemaAction.Error == missingSchemaAction) {
- throw ExceptionBuilder.MergeMissingDefinition(table.TableName);
- }
- }
- else {
- if (MissingSchemaAction.Ignore != missingSchemaAction) {
- // Do the columns
- int oldCount = targetTable.Columns.Count;
- for (int i = 0; i < table.Columns.Count; i++) {
- DataColumn src = table.Columns[i];
- DataColumn dest = (targetTable.Columns.Contains(src.ColumnName, true)) ? targetTable.Columns[src.ColumnName] : null;
- if (dest == null) {
- if (MissingSchemaAction.Add == missingSchemaAction) {
- dest = src.Clone();
- targetTable.Columns.Add(dest);
- }
- else {
- if (!isStandAlonetable)
- dataSet.RaiseMergeFailed(targetTable, Res.GetString(Res.DataMerge_MissingColumnDefinition, table.TableName, src.ColumnName), missingSchemaAction);
- else
- throw ExceptionBuilder.MergeFailed(Res.GetString(Res.DataMerge_MissingColumnDefinition, table.TableName, src.ColumnName));
- }
- }
- else {
- if (dest.DataType != src.DataType ||
- ((dest.DataType == typeof(DateTime)) && (dest.DateTimeMode != src.DateTimeMode) && ((dest.DateTimeMode & src.DateTimeMode) != DataSetDateTime.Unspecified))) {
- if (!isStandAlonetable)
- dataSet.RaiseMergeFailed(targetTable, Res.GetString(Res.DataMerge_DataTypeMismatch, src.ColumnName), MissingSchemaAction.Error);
- else
- throw ExceptionBuilder.MergeFailed(Res.GetString(Res.DataMerge_DataTypeMismatch, src.ColumnName));
- }
- //
- MergeExtendedProperties(src.ExtendedProperties, dest.ExtendedProperties);
- }
- }
-
- // Set DataExpression
- if (isStandAlonetable) {
- for (int i = oldCount; i < targetTable.Columns.Count; i++) {
- targetTable.Columns[i].Expression = table.Columns[targetTable.Columns[i].ColumnName].Expression;
- }
- }
- // check the PrimaryKey
- DataColumn[] targetPKey = targetTable.PrimaryKey;
- DataColumn[] tablePKey = table.PrimaryKey;
- if (targetPKey.Length != tablePKey.Length) {
- // special case when the target table does not have the PrimaryKey
- if (targetPKey.Length == 0) {
- DataColumn[] key = new DataColumn[tablePKey.Length];
- for (int i = 0; i < tablePKey.Length; i++) {
- key[i] = targetTable.Columns[tablePKey[i].ColumnName];
- }
- targetTable.PrimaryKey = key;
- }
- else if (tablePKey.Length != 0) {
- dataSet.RaiseMergeFailed(targetTable, Res.GetString(Res.DataMerge_PrimaryKeyMismatch), missingSchemaAction);
- }
- }
- else {
- for (int i = 0; i < targetPKey.Length; i++) {
- if (String.Compare(targetPKey[i].ColumnName, tablePKey[i].ColumnName, false, targetTable.Locale) != 0) {
- dataSet.RaiseMergeFailed(table,
- Res.GetString(Res.DataMerge_PrimaryKeyColumnsMismatch, targetPKey[i].ColumnName, tablePKey[i].ColumnName),
- missingSchemaAction
- );
- }
- }
- }
- }
- MergeExtendedProperties(table.ExtendedProperties, targetTable.ExtendedProperties);
- }
- return targetTable;
- }
- private void MergeTableData(DataTable src) {
- DataTable dest = MergeSchema(src);
- if (dest == null) return;
- dest.MergingData = true;
- try {
- MergeTable(src, dest);
- }
- finally {
- dest.MergingData = false;
- }
- }
- private void MergeConstraints(DataSet source) {
- for (int i = 0; i < source.Tables.Count; i ++) {
- MergeConstraints(source.Tables[i]);
- }
- }
- private void MergeConstraints(DataTable table) {
- // Merge constraints
- for (int i = 0; i < table.Constraints.Count; i++) {
- Constraint src = table.Constraints[i];
- Constraint dest = src.Clone(dataSet, _IgnoreNSforTableLookup);
- if (dest == null) {
- dataSet.RaiseMergeFailed(table,
- Res.GetString(Res.DataMerge_MissingConstraint, src.GetType().FullName, src.ConstraintName),
- missingSchemaAction
- );
- }
- else {
- Constraint cons = dest.Table.Constraints.FindConstraint(dest);
- if (cons == null) {
- if (MissingSchemaAction.Add == missingSchemaAction) {
- try {
- // try to keep the original name
- dest.Table.Constraints.Add(dest);
- }
- catch (DuplicateNameException) {
- // if fail, assume default name
- dest.ConstraintName = "";
- dest.Table.Constraints.Add(dest);
- }
- }
- else if (MissingSchemaAction.Error == missingSchemaAction) {
- dataSet.RaiseMergeFailed(table,
- Res.GetString(Res.DataMerge_MissingConstraint, src.GetType().FullName, src.ConstraintName),
- missingSchemaAction
- );
- }
- }
- else {
- MergeExtendedProperties(src.ExtendedProperties, cons.ExtendedProperties);
- }
- }
- }
- }
- private void MergeRelation(DataRelation relation) {
- Debug.Assert(MissingSchemaAction.Error == missingSchemaAction ||
- MissingSchemaAction.Add == missingSchemaAction,
- "Unexpected value of MissingSchemaAction parameter : " + ((Enum) missingSchemaAction).ToString());
- DataRelation destRelation = null;
- // try to find given relation in this dataSet
- int iDest = dataSet.Relations.InternalIndexOf(relation.RelationName);
- if (iDest >= 0) {
- // check the columns and Relation properties..
- destRelation = dataSet.Relations[iDest];
- if (relation.ParentKey.ColumnsReference.Length != destRelation.ParentKey.ColumnsReference.Length) {
- dataSet.RaiseMergeFailed(null,
- Res.GetString(Res.DataMerge_MissingDefinition, relation.RelationName),
- missingSchemaAction
- );
- }
- for (int i = 0; i < relation.ParentKey.ColumnsReference.Length; i++) {
- DataColumn dest = destRelation.ParentKey.ColumnsReference[i];
- DataColumn src = relation.ParentKey.ColumnsReference[i];
- if (0 != string.Compare(dest.ColumnName, src.ColumnName, false, dest.Table.Locale)) {
- dataSet.RaiseMergeFailed(null,
- Res.GetString(Res.DataMerge_ReltionKeyColumnsMismatch, relation.RelationName),
- missingSchemaAction
- );
- }
- dest = destRelation.ChildKey.ColumnsReference[i];
- src = relation.ChildKey.ColumnsReference[i];
- if (0 != string.Compare(dest.ColumnName, src.ColumnName, false, dest.Table.Locale)) {
- dataSet.RaiseMergeFailed(null,
- Res.GetString(Res.DataMerge_ReltionKeyColumnsMismatch, relation.RelationName),
- missingSchemaAction
- );
- }
- }
- }
- else {
- if (MissingSchemaAction.Add == missingSchemaAction) {
- // create identical realtion in the current dataset
- DataTable parent;
- if (_IgnoreNSforTableLookup){
- parent = dataSet.Tables[relation.ParentTable.TableName];
- }
- else {
- parent = dataSet.Tables[relation.ParentTable.TableName, relation.ParentTable.Namespace];
- }
- DataTable child;
- if (_IgnoreNSforTableLookup) {
- child = dataSet.Tables[relation.ChildTable.TableName];
- }
- else {
- child = dataSet.Tables[relation.ChildTable.TableName,relation.ChildTable.Namespace];
- }
-
- DataColumn[] parentColumns = new DataColumn[relation.ParentKey.ColumnsReference.Length];
- DataColumn[] childColumns = new DataColumn[relation.ParentKey.ColumnsReference.Length];
- for (int i = 0; i < relation.ParentKey.ColumnsReference.Length; i++) {
- parentColumns[i] = parent.Columns[relation.ParentKey.ColumnsReference[i].ColumnName];
- childColumns[i] = child.Columns[relation.ChildKey.ColumnsReference[i].ColumnName];
- }
- try {
- destRelation = new DataRelation(relation.RelationName, parentColumns, childColumns, relation.createConstraints);
- destRelation.Nested = relation.Nested;
- dataSet.Relations.Add(destRelation);
- }
- catch (Exception e) {
- //
- if (!Common.ADP.IsCatchableExceptionType(e)) {
- throw;
- }
- ExceptionBuilder.TraceExceptionForCapture(e);
- dataSet.RaiseMergeFailed(null, e.Message, missingSchemaAction);
- }
- }
- else {
- Debug.Assert(MissingSchemaAction.Error == missingSchemaAction, "Unexpected value of MissingSchemaAction parameter : " + ((Enum) missingSchemaAction).ToString());
- throw ExceptionBuilder.MergeMissingDefinition(relation.RelationName);
- }
- }
- MergeExtendedProperties(relation.ExtendedProperties, destRelation.ExtendedProperties);
- return;
- }
- private void MergeExtendedProperties(PropertyCollection src, PropertyCollection dst) {
- if (MissingSchemaAction.Ignore == missingSchemaAction) {
- return;
- }
- IDictionaryEnumerator srcDE = src.GetEnumerator();
- while (srcDE.MoveNext()) {
- if (!preserveChanges || dst[srcDE.Key] == null)
- dst[srcDE.Key] = srcDE.Value;
- }
- }
- private DataKey GetSrcKey(DataTable src, DataTable dst) {
- if (src.primaryKey != null)
- return src.primaryKey.Key;
- DataKey key = default(DataKey);
- if (dst.primaryKey != null) {
- DataColumn[] dstColumns = dst.primaryKey.Key.ColumnsReference;
- DataColumn[] srcColumns = new DataColumn[dstColumns.Length];
- for (int j = 0; j < dstColumns.Length; j++) {
- srcColumns[j] = src.Columns[dstColumns[j].ColumnName];
- }
- key = new DataKey(srcColumns, false); // DataKey will take ownership of srcColumns
- }
- return key;
- }
- }
- }
|