PageRenderTime 89ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.Data/System.Data/MergeManager.cs

https://github.com/iainlane/mono
C# | 406 lines | 295 code | 60 blank | 51 comment | 103 complexity | 8c4dca6bdfea8717a6d8b6598ee286dd MD5 | raw file
  1. //
  2. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining
  5. // a copy of this software and associated documentation files (the
  6. // "Software"), to deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge, publish,
  8. // distribute, sublicense, and/or sell copies of the Software, and to
  9. // permit persons to whom the Software is furnished to do so, subject to
  10. // the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be
  13. // included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. //
  23. using System;
  24. using System.Collections;
  25. namespace System.Data
  26. {
  27. /// <summary>
  28. /// Summary description for MergeManager.
  29. /// </summary>
  30. internal class MergeManager
  31. {
  32. internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
  33. {
  34. if(targetSet == null)
  35. throw new ArgumentNullException("targetSet");
  36. if(sourceSet == null)
  37. throw new ArgumentNullException("sourceSet");
  38. if (sourceSet == targetSet)
  39. return;
  40. bool prevEC = targetSet.EnforceConstraints;
  41. targetSet.EnforceConstraints = false;
  42. foreach (DataTable t in sourceSet.Tables)
  43. MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
  44. AdjustSchemaRelations (targetSet, sourceSet, missingSchemaAction);
  45. targetSet.EnforceConstraints = prevEC;
  46. }
  47. internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
  48. {
  49. if(targetSet == null)
  50. throw new ArgumentNullException("targetSet");
  51. if(sourceTable == null)
  52. throw new ArgumentNullException("sourceTable");
  53. if (sourceTable.DataSet == targetSet)
  54. return;
  55. bool savedEnfoceConstraints = targetSet.EnforceConstraints;
  56. targetSet.EnforceConstraints = false;
  57. DataTable targetTable = null;
  58. if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
  59. return;
  60. if (targetTable != null)
  61. fillData(targetTable, sourceTable, preserveChanges);
  62. targetSet.EnforceConstraints = savedEnfoceConstraints;
  63. }
  64. internal static void Merge (DataTable targetTable,
  65. DataTable sourceTable,
  66. bool preserveChanges,
  67. MissingSchemaAction missingSchemaAction)
  68. {
  69. if(targetTable== null)
  70. throw new ArgumentNullException("targetTable");
  71. if(sourceTable == null)
  72. throw new ArgumentNullException("sourceTable");
  73. if (sourceTable == targetTable)
  74. return;
  75. bool savedEnforceConstraints = targetTable.EnforceConstraints;
  76. targetTable.EnforceConstraints = false;
  77. if (!AdjustSchema(targetTable, sourceTable, missingSchemaAction))
  78. return;
  79. fillData(targetTable, sourceTable, preserveChanges);
  80. targetTable.EnforceConstraints = savedEnforceConstraints;
  81. }
  82. internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
  83. {
  84. if(targetSet == null)
  85. throw new ArgumentNullException("targetSet");
  86. if(sourceRows == null)
  87. throw new ArgumentNullException("sourceRows");
  88. bool savedEnfoceConstraints = targetSet.EnforceConstraints;
  89. targetSet.EnforceConstraints = false;
  90. ArrayList targetTables = new ArrayList();
  91. for (int i = 0; i < sourceRows.Length; i++) {
  92. DataRow row = sourceRows[i];
  93. DataTable sourceTable = row.Table;
  94. DataTable targetTable = null;
  95. if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
  96. return;
  97. if (targetTable != null) {
  98. MergeRow(targetTable, row, preserveChanges);
  99. if (!(targetTables.IndexOf(targetTable) >= 0))
  100. targetTables.Add(targetTable);
  101. }
  102. }
  103. targetSet.EnforceConstraints = savedEnfoceConstraints;
  104. }
  105. // merge a row into a target table.
  106. private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)
  107. {
  108. DataColumn[] primaryKeys = targetTable.PrimaryKey;
  109. DataRow targetRow = null;
  110. DataRowVersion version = DataRowVersion.Default;
  111. if (row.RowState == DataRowState.Deleted)
  112. version = DataRowVersion.Original;
  113. if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.
  114. {
  115. // initiate an array that has the values of the primary keys.
  116. object[] keyValues = new object[primaryKeys.Length];
  117. for (int j = 0; j < keyValues.Length; j++)
  118. {
  119. keyValues[j] = row[primaryKeys[j].ColumnName, version];
  120. }
  121. // find the row in the target table.
  122. targetRow = targetTable.Rows.Find(keyValues, DataViewRowState.OriginalRows);
  123. if (targetRow == null)
  124. targetRow = targetTable.Rows.Find(keyValues);
  125. }
  126. // row doesn't exist in target table, or there are no primary keys.
  127. // create new row and copy values from source row to the new row.
  128. if (targetRow == null)
  129. {
  130. DataRow newRow = targetTable.NewNotInitializedRow();
  131. // Don't check for ReadOnly, when cloning data to new uninitialized row.
  132. row.CopyValuesToRow(newRow, false);
  133. targetTable.Rows.AddInternal (newRow);
  134. }
  135. // row exists in target table, and presere changes is false -
  136. // change the values of the target row to the values of the source row.
  137. else
  138. {
  139. row.MergeValuesToRow(targetRow, preserveChanges);
  140. }
  141. }
  142. private static bool AdjustSchemaRelations (DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
  143. {
  144. if (missingSchemaAction == MissingSchemaAction.Ignore)
  145. return true;
  146. foreach(DataTable sourceTable in sourceSet.Tables) {
  147. DataTable targetTable = targetSet.Tables[sourceTable.TableName];
  148. if (targetTable == null)
  149. continue;
  150. foreach (Constraint constraint in sourceTable.Constraints) {
  151. Constraint targetConstraint = null;
  152. string constraintName = constraint.ConstraintName;
  153. if (targetTable.Constraints.Contains (constraintName))
  154. constraintName = "";
  155. UniqueConstraint uc = constraint as UniqueConstraint;
  156. // PrimaryKey is already taken care of while merging the table
  157. // ForeignKey constraint takes care of Parent Unique Constraints
  158. if (uc != null) {
  159. if (uc.IsPrimaryKey || uc.ChildConstraint != null)
  160. continue;
  161. DataColumn[] columns = ResolveColumns (targetTable, uc.Columns);
  162. targetConstraint = new UniqueConstraint (constraintName, columns, false);
  163. }
  164. ForeignKeyConstraint fc = constraint as ForeignKeyConstraint;
  165. if (fc != null) {
  166. DataColumn[] columns = ResolveColumns (targetTable, fc.Columns);
  167. DataColumn[] relatedColumns = ResolveColumns (targetSet.Tables [fc.RelatedTable.TableName],
  168. fc.RelatedColumns);
  169. targetConstraint = new ForeignKeyConstraint (constraintName, relatedColumns, columns);
  170. }
  171. bool dupConstraintFound = false;
  172. foreach (Constraint cons in targetTable.Constraints) {
  173. if (!targetConstraint.Equals (cons))
  174. continue;
  175. dupConstraintFound = true;
  176. break;
  177. }
  178. // If equivalent-constraint already exists, then just do nothing
  179. if (dupConstraintFound)
  180. continue;
  181. if (missingSchemaAction == MissingSchemaAction.Error)
  182. throw new DataException ("Target DataSet missing " + targetConstraint.GetType () +
  183. targetConstraint.ConstraintName);
  184. else
  185. targetTable.Constraints.Add (targetConstraint);
  186. }
  187. }
  188. foreach (DataRelation relation in sourceSet.Relations) {
  189. DataRelation targetRelation = targetSet.Relations [relation.RelationName];
  190. if (targetRelation == null) {
  191. if (missingSchemaAction == MissingSchemaAction.Error)
  192. throw new ArgumentException ("Target DataSet mising definition for " +
  193. relation.RelationName);
  194. DataColumn[] parentColumns = ResolveColumns (targetSet.Tables [relation.ParentTable.TableName],
  195. relation.ParentColumns);
  196. DataColumn[] childColumns = ResolveColumns (targetSet.Tables [relation.ChildTable.TableName],
  197. relation.ChildColumns);
  198. targetRelation = targetSet.Relations.Add (relation.RelationName, parentColumns,
  199. childColumns, relation.createConstraints);
  200. targetRelation.Nested = relation.Nested;
  201. } else if (!CompareColumnArrays (relation.ParentColumns, targetRelation.ParentColumns) ||
  202. !CompareColumnArrays (relation.ChildColumns, targetRelation.ChildColumns)) {
  203. RaiseMergeFailedEvent (null, "Relation " + relation.RelationName +
  204. " cannot be merged, because keys have mismatch columns.");
  205. }
  206. }
  207. return true;
  208. }
  209. private static DataColumn[] ResolveColumns(DataTable targetTable, DataColumn[] sourceColumns)
  210. {
  211. if (sourceColumns != null && sourceColumns.Length > 0) {
  212. // lets just assume that all columns are from the Same table
  213. if (targetTable != null) {
  214. int i=0;
  215. DataColumn[] targetColumns = new DataColumn[sourceColumns.Length];
  216. DataColumn tmpCol ;
  217. foreach(DataColumn sourceColumn in sourceColumns) {
  218. tmpCol = targetTable.Columns[sourceColumn.ColumnName];
  219. if (tmpCol == null)
  220. throw new DataException ("Column " + sourceColumn.ColumnName +
  221. " does not belong to table " + targetTable.TableName);
  222. targetColumns [i++] = tmpCol;
  223. }
  224. return targetColumns;
  225. }
  226. }
  227. return null;
  228. }
  229. // adjust the table schema according to the missingschemaaction param.
  230. // return false if adjusting fails.
  231. private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction, ref DataTable newTable)
  232. {
  233. // if the source table not exists in the target dataset
  234. // we act according to the missingschemaaction param.
  235. DataTable targetTable = targetSet.Tables [sourceTable.TableName];
  236. if (targetTable == null) {
  237. if (missingSchemaAction == MissingSchemaAction.Ignore)
  238. return true;
  239. if (missingSchemaAction == MissingSchemaAction.Error)
  240. throw new ArgumentException ("Target DataSet missing definition for " +
  241. sourceTable.TableName + ".");
  242. targetTable = (DataTable)sourceTable.Clone();
  243. targetSet.Tables.Add(targetTable);
  244. }
  245. AdjustSchema (targetTable, sourceTable, missingSchemaAction);
  246. newTable = targetTable;
  247. return true;
  248. }
  249. private static bool AdjustSchema(DataTable targetTable, DataTable sourceTable, MissingSchemaAction missingSchemaAction)
  250. {
  251. if (missingSchemaAction == MissingSchemaAction.Ignore)
  252. return true;
  253. for (int i = 0; i < sourceTable.Columns.Count; i++) {
  254. DataColumn sourceColumn = sourceTable.Columns[i];
  255. // if a column from the source table doesn't exists in the target table
  256. // we act according to the missingschemaaction param.
  257. DataColumn targetColumn = targetTable.Columns [sourceColumn.ColumnName];
  258. if(targetColumn == null) {
  259. if (missingSchemaAction == MissingSchemaAction.Error)
  260. throw new DataException ("Target table " + targetTable.TableName +
  261. " missing definition for column " + sourceColumn.ColumnName);
  262. targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType,
  263. sourceColumn.Expression, sourceColumn.ColumnMapping);
  264. targetTable.Columns.Add(targetColumn);
  265. }
  266. if(sourceColumn.AutoIncrement) {
  267. targetColumn.AutoIncrement = sourceColumn.AutoIncrement;
  268. targetColumn.AutoIncrementSeed = sourceColumn.AutoIncrementSeed;
  269. targetColumn.AutoIncrementStep = sourceColumn.AutoIncrementStep;
  270. }
  271. }
  272. if (!AdjustPrimaryKeys(targetTable, sourceTable))
  273. return false;
  274. checkColumnTypes (targetTable, sourceTable);
  275. return true;
  276. }
  277. // find if there is a valid matching between the targetTable PrimaryKey and the
  278. // sourceTable primatyKey.
  279. // return true if there is a match, else return false and raise a MergeFailedEvent.
  280. private static bool AdjustPrimaryKeys(DataTable targetTable, DataTable sourceTable)
  281. {
  282. if (sourceTable.PrimaryKey.Length == 0)
  283. return true;
  284. // If targetTable does not have a PrimaryKey, just import the sourceTable PrimaryKey
  285. if (targetTable.PrimaryKey.Length == 0) {
  286. DataColumn[] targetColumns = ResolveColumns (targetTable, sourceTable.PrimaryKey);
  287. targetTable.PrimaryKey = targetColumns;
  288. return true;
  289. }
  290. // If both the tables have a primary key, verify that they are equivalent.
  291. // raise a MergeFailedEvent if the keys are not equivalent
  292. if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
  293. RaiseMergeFailedEvent (targetTable, "<target>.PrimaryKey and <source>.PrimaryKey have different Length.");
  294. return false;
  295. }
  296. for (int i=0; i < targetTable.PrimaryKey.Length; ++i) {
  297. if (targetTable.PrimaryKey [i].ColumnName.Equals (sourceTable.PrimaryKey [i].ColumnName))
  298. continue;
  299. RaiseMergeFailedEvent (targetTable, "Mismatch columns in the PrimaryKey : <target>." +
  300. targetTable.PrimaryKey [i].ColumnName + " versus <source>." + sourceTable.PrimaryKey [i].ColumnName);
  301. return false;
  302. }
  303. return true;
  304. }
  305. // fill the data from the source table to the target table
  306. private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)
  307. {
  308. for (int i = 0; i < sourceTable.Rows.Count; i++)
  309. {
  310. DataRow row = sourceTable.Rows[i];
  311. MergeRow(targetTable, row, preserveChanges);
  312. }
  313. }
  314. // check tha column from 2 tables that has the same name also has the same datatype.
  315. private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)
  316. {
  317. for (int i = 0; i < sourceTable.Columns.Count; i++)
  318. {
  319. DataColumn fromCol = sourceTable.Columns[i];
  320. DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
  321. if (toCol == null)
  322. continue;
  323. if (toCol.DataTypeMatches (fromCol))
  324. continue;
  325. throw new DataException("<target>." + fromCol.ColumnName + " and <source>." +
  326. fromCol.ColumnName + " have conflicting properties: DataType " +
  327. " property mismatch.");
  328. }
  329. }
  330. private static bool CompareColumnArrays (DataColumn[] arr1, DataColumn[] arr2)
  331. {
  332. if (arr1.Length != arr2.Length)
  333. return false;
  334. for (int i=0; i < arr1.Length; ++i)
  335. if (!arr1 [i].ColumnName.Equals (arr2 [i].ColumnName))
  336. return false;
  337. return true;
  338. }
  339. private static void RaiseMergeFailedEvent (DataTable targetTable, string errMsg)
  340. {
  341. MergeFailedEventArgs args = new MergeFailedEventArgs (targetTable, errMsg);
  342. if (targetTable.DataSet != null)
  343. targetTable.DataSet.OnMergeFailed (args);
  344. }
  345. }
  346. }