2003-11-09 Pedro Mart�nez Juli� <yoros@wanadoo.es>
[mono.git] / mcs / class / System.Data / System.Data / MergeManager.cs
1 using System;\r
2 \r
3 namespace System.Data\r
4 {\r
5         /// <summary>\r
6         /// Summary description for MergeManager.\r
7         /// </summary>\r
8         internal class MergeManager\r
9         {\r
10                 internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
11                 {\r
12                         if(targetSet == null)\r
13                                 throw new ArgumentNullException("targetSet");\r
14                         if(sourceSet == null)\r
15                                 throw new ArgumentNullException("sourceSet");\r
16 \r
17                         foreach (DataTable t in sourceSet.Tables)\r
18                                 MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);\r
19 \r
20                 }\r
21 \r
22                 internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
23                 {\r
24                         if(targetSet == null)\r
25                                 throw new ArgumentNullException("targetSet");\r
26                         if(sourceTable == null)\r
27                                 throw new ArgumentNullException("sourceTable");\r
28 \r
29                         \r
30                         if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))\r
31                                 return;\r
32                         DataTable targetTable = targetSet.Tables[sourceTable.TableName];\r
33                         if (targetTable != null)\r
34                         {\r
35                                 checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same\r
36                                 fillData(targetTable, sourceTable, preserveChanges);\r
37                         }\r
38                         \r
39                 }\r
40 \r
41                 internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
42                 {\r
43                         if(targetSet == null)\r
44                                 throw new ArgumentNullException("targetSet");\r
45                         if(sourceRows == null)\r
46                                 throw new ArgumentNullException("sourceRows");\r
47 \r
48                         for (int i = 0; i < sourceRows.Length; i++)\r
49                         {\r
50                                 DataRow row = sourceRows[i];\r
51                                 DataTable sourceTable = row.Table;\r
52                                 if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))\r
53                                         return;\r
54                                 DataTable targetTable = targetSet.Tables[row.Table.TableName];\r
55                                 if (targetTable != null)\r
56                                 {\r
57                                         checkColumnTypes(targetTable, row.Table);\r
58                                         MergeRow(targetTable, row, preserveChanges);\r
59                                 }\r
60                         }\r
61                 }\r
62 \r
63                 // merge a row into a target table.\r
64                 private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)\r
65                 {\r
66                         DataColumnCollection columns = row.Table.Columns;\r
67                         DataColumn[] primaryKeys = targetTable.PrimaryKey;\r
68                         DataRow targetRow = null;\r
69                         DataRowVersion version = DataRowVersion.Default;\r
70                         if (row.RowState == DataRowState.Deleted)\r
71                                 version = DataRowVersion.Original;\r
72 \r
73                         if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.\r
74                         {\r
75                                 // initiate an array that has the values of the primary keys.\r
76                                 object[] keyValues = new object[primaryKeys.Length];\r
77                                 \r
78                                 for (int j = 0; j < keyValues.Length; j++)\r
79                                 {\r
80                                         keyValues[j] = row[primaryKeys[j].ColumnName, version];\r
81                                 }\r
82                         \r
83                                 // find the row in the target table.\r
84                                 targetRow = targetTable.Rows.Find(keyValues);\r
85                         }\r
86                         // row doesn't exist in target table, or there are no primary keys.\r
87                         // create new row and copy values from source row to the new row.\r
88                         if (targetRow == null)\r
89                         { \r
90                                 targetRow = targetTable.NewRow();\r
91                                 row.CopyValuesToRow(targetRow);\r
92                                 targetTable.Rows.Add(targetRow);\r
93                         }\r
94                         // row exists in target table, and presere changes is false - \r
95                         // change the values of the target row to the values of the source row.\r
96                         else if (!preserveChanges)\r
97                         {\r
98                                 row.CopyValuesToRow(targetRow);\r
99                         }\r
100 \r
101                 }\r
102                         \r
103                 \r
104                 \r
105                 // adjust the table schema according to the missingschemaaction param.\r
106                 // return false if adjusting fails.\r
107                 private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction)\r
108                 {\r
109                         string tableName = sourceTable.TableName;\r
110                         \r
111                         // if the source table not exists in the target dataset\r
112                         // we act according to the missingschemaaction param.\r
113                         if (!targetSet.Tables.Contains(tableName))\r
114                         {\r
115                                 if (missingSchemaAction == MissingSchemaAction.Ignore)\r
116                                         return true;\r
117                                 if (missingSchemaAction == MissingSchemaAction.Error)\r
118                                         throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");\r
119                                 targetSet.Tables.Add((DataTable)sourceTable.Clone());\r
120                         }\r
121                         \r
122                         DataTable table = targetSet.Tables[tableName];\r
123                         \r
124                         if (!CheckPrimaryKeys(table, sourceTable))\r
125                                 return false;\r
126                         \r
127                         for (int i = 0; i < sourceTable.Columns.Count; i++)\r
128                         {\r
129                                 DataColumn col = sourceTable.Columns[i];\r
130                                 // if a column from the source table doesn't exists in the target table\r
131                                 // we act according to the missingschemaaction param.\r
132                                 if(!table.Columns.Contains(col.ColumnName))\r
133                                 {\r
134                                         if (missingSchemaAction == MissingSchemaAction.Ignore)\r
135                                                 continue;\r
136                                         if (missingSchemaAction == MissingSchemaAction.Error)\r
137                                                 throw new ArgumentException(("Column '" + col.ColumnName + "' does not belong to table Items."));\r
138                                         \r
139                                         table.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));\r
140                                 }\r
141                         }\r
142 \r
143                         return true;\r
144                 }\r
145                 \r
146                 // find if there is a valid matching between the targetTable PrimaryKey and the\r
147                 // sourceTable primatyKey.\r
148                 // return true if there is a match, else return false and raise a MergeFailedEvent.\r
149                 private static bool CheckPrimaryKeys(DataTable targetTable, DataTable sourceTable)\r
150                 {\r
151                         // if the length of one of the tables primarykey if 0 - there is nothing to check.\r
152                         if (targetTable.PrimaryKey.Length != 0 && sourceTable.PrimaryKey.Length != 0)\r
153                         {\r
154                                 // if the length of primarykey is not equal - merge fails\r
155                                 if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length)\r
156                                 {\r
157                                         string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";\r
158                                         MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);\r
159                                         targetTable.DataSet.OnMergeFailed(e);\r
160                                         return false;\r
161                                 }\r
162                                 else\r
163                                 {\r
164                                         // we have to see that each primary column in the target table\r
165                                         // has a column with the same name in the sourcetable primarykey columns. \r
166                                         bool foundMatch;\r
167                                         DataColumn[] targetDataColumns = targetTable.PrimaryKey;\r
168                                         DataColumn[] srcDataColumns = sourceTable.PrimaryKey;\r
169 \r
170                                         // loop on all primary key columns in the targetTable.\r
171                                         for (int i = 0; i < targetDataColumns.Length; i++)\r
172                                         {\r
173                                                 foundMatch = false;\r
174                                                 DataColumn col = targetDataColumns[i];\r
175 \r
176                                                 // find if there is a column with the same name in the \r
177                                                 // sourceTable primary key columns.\r
178                                                 for (int j = 0; j < srcDataColumns.Length; j++)\r
179                                                 {\r
180                                                         if (srcDataColumns[j].ColumnName == col.ColumnName)\r
181                                                         {\r
182                                                                 foundMatch = true;\r
183                                                                 break;\r
184                                                         }\r
185                                                 }\r
186                                                 if (!foundMatch)\r
187                                                 {\r
188                                                         string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";\r
189                                                         MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);\r
190                                                         targetTable.DataSet.OnMergeFailed(e);\r
191                                                         return false;\r
192                                                 }\r
193                                                 \r
194                                         }\r
195                                 }\r
196                         }\r
197 \r
198                         return true;\r
199                 }\r
200                 \r
201                 // fill the data from the source table to the target table\r
202                 private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)\r
203                 {\r
204                         for (int i = 0; i < sourceTable.Rows.Count; i++)\r
205                         {\r
206                                 DataRow row = sourceTable.Rows[i];\r
207                                 MergeRow(targetTable, row, preserveChanges);\r
208                         }\r
209                 }\r
210                 \r
211                 // check tha column from 2 tables that has the same name also has the same datatype.\r
212                 private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)\r
213                 {\r
214                         for (int i = 0; i < sourceTable.Columns.Count; i++)\r
215                         {\r
216                                 DataColumn fromCol = sourceTable.Columns[i];\r
217                                 DataColumn toCol = targetTable.Columns[fromCol.ColumnName];\r
218                                 if((toCol != null) && (toCol.DataType != fromCol.DataType))\r
219                                         throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");\r
220                         }\r
221                 }\r
222         }\r
223 }\r