[LoongArch64] Part-5:add loongarch support in some files for LoongArch64. (#21769)
[mono-project.git] / mcs / class / referencesource / System.Data / System / Data / DataTable.cs
blob6c841445c7ac1edb77759c490baf8754a27a8e3f
1 //------------------------------------------------------------------------------
2 // <copyright file="DataTable.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data {
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.ComponentModel;
14 using System.Diagnostics;
15 using System.Globalization;
16 using System.IO;
17 using System.Runtime.Serialization;
18 using System.Text;
19 using System.Threading;
20 using System.Xml;
21 using System.Xml.Schema;
22 using System.Xml.Serialization;
23 using System.Data.Common;
24 using System.Runtime.Versioning;
25 using System.Runtime.CompilerServices;
27 /// <devdoc>
28 /// <para>Represents one table of in-memory data.</para>
29 /// </devdoc>
31 ToolboxItem(false),
32 DesignTimeVisible(false),
33 DefaultProperty("TableName"),
34 Editor("Microsoft.VSDesigner.Data.Design.DataTableEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
35 DefaultEvent("RowChanging"),
36 XmlSchemaProvider("GetDataTableSchema"),
37 Serializable
39 public class DataTable : MarshalByValueComponent, System.ComponentModel.IListSource, ISupportInitializeNotification, ISerializable, IXmlSerializable{
40 private DataSet dataSet;
41 private DataView defaultView = null;
43 // rows
44 /// <summary>
45 /// Monotonically increasing number representing the order <see cref="DataRow"/> have been added to <see cref="DataRowCollection"/>.
46 /// </summary>
47 /// <remarks>This limits <see cref="DataRowCollection.Add(DataRow)"/> to <see cref="Int32.MaxValue"/> operations.</remarks>
48 internal long nextRowID;
49 internal readonly DataRowCollection rowCollection;
51 // columns
52 internal readonly DataColumnCollection columnCollection;
54 // constraints
55 private readonly ConstraintCollection constraintCollection;
57 //SimpleContent implementation
58 private int elementColumnCount = 0;
60 // relations
61 internal DataRelationCollection parentRelationsCollection;
62 internal DataRelationCollection childRelationsCollection;
64 // RecordManager
65 internal readonly RecordManager recordManager;
67 // index mgmt
68 internal readonly List<Index> indexes;
70 private List<Index> shadowIndexes;
71 private int shadowCount;
73 // props
74 internal PropertyCollection extendedProperties = null;
75 private string tableName = "";
76 internal string tableNamespace = null;
77 private string tablePrefix = "";
78 internal DataExpression displayExpression;
79 internal bool fNestedInDataset = true;
81 // globalization stuff
82 private CultureInfo _culture;
83 private bool _cultureUserSet;
84 private CompareInfo _compareInfo;
85 private CompareOptions _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
86 private IFormatProvider _formatProvider;
87 private StringComparer _hashCodeProvider;
88 private bool _caseSensitive;
89 private bool _caseSensitiveUserSet;
91 // XML properties
92 internal string encodedTableName; // For XmlDataDocument only
93 internal DataColumn xmlText; // text values of a complex xml element
94 internal DataColumn _colUnique;
95 internal bool textOnly = false; // the table has only text value with possible attributes
96 internal decimal minOccurs = 1; // default = 1
97 internal decimal maxOccurs = 1; // default = 1
98 internal bool repeatableElement = false;
99 private object typeName = null;
101 // primary key info
102 private readonly static Int32[] zeroIntegers = new Int32[0];
103 internal readonly static DataColumn[] zeroColumns = new DataColumn[0];
104 internal readonly static DataRow[] zeroRows = new DataRow[0];
105 internal UniqueConstraint primaryKey;
106 internal readonly static IndexField[] zeroIndexField = new IndexField[0];
107 internal IndexField[] _primaryIndex = zeroIndexField;
108 private DataColumn[] delayedSetPrimaryKey = null;
110 // Loading Schema and/or Data related optimization
111 private Index loadIndex;
112 private Index loadIndexwithOriginalAdded = null;
113 private Index loadIndexwithCurrentDeleted = null;
114 private int _suspendIndexEvents;
116 private bool savedEnforceConstraints = false;
117 private bool inDataLoad = false;
118 private bool initialLoad;
119 private bool schemaLoading = false;
120 private bool enforceConstraints = true;
121 internal bool _suspendEnforceConstraints = false;
123 protected internal bool fInitInProgress = false;
124 private bool inLoad = false;
125 internal bool fInLoadDiffgram = false;
127 private byte _isTypedDataTable; // 0 == unknown, 1 = yes, 2 = No
128 private DataRow[] EmptyDataRowArray;
131 // Property Descriptor Cache for DataBinding
132 private PropertyDescriptorCollection propertyDescriptorCollectionCache = null;
134 // Cache for relation that has this table as nested child table.
135 private static readonly DataRelation[] EmptyArrayDataRelation = new DataRelation[0];
136 private DataRelation[] _nestedParentRelations = EmptyArrayDataRelation;
138 // Dependent column list for expression evaluation
139 internal List<DataColumn> dependentColumns = null;
141 // events
142 private bool mergingData = false;
143 private DataRowChangeEventHandler onRowChangedDelegate;
144 private DataRowChangeEventHandler onRowChangingDelegate;
145 private DataRowChangeEventHandler onRowDeletingDelegate;
146 private DataRowChangeEventHandler onRowDeletedDelegate;
147 private DataColumnChangeEventHandler onColumnChangedDelegate;
148 private DataColumnChangeEventHandler onColumnChangingDelegate;
150 private DataTableClearEventHandler onTableClearingDelegate;
151 private DataTableClearEventHandler onTableClearedDelegate;
152 private DataTableNewRowEventHandler onTableNewRowDelegate;
154 private PropertyChangedEventHandler onPropertyChangingDelegate;
156 private System.EventHandler onInitialized;
159 // misc
160 private readonly DataRowBuilder rowBuilder;
161 private const String KEY_XMLSCHEMA = "XmlSchema";
162 private const String KEY_XMLDIFFGRAM = "XmlDiffGram";
163 private const String KEY_NAME = "TableName";
165 internal readonly List<DataView> delayedViews = new List<DataView>();
166 private readonly List<DataViewListener> _dataViewListeners = new List<DataViewListener>();
168 // private bool serializeHierarchy = false;
169 internal Hashtable rowDiffId = null;
170 internal readonly ReaderWriterLock indexesLock = new ReaderWriterLock();
171 internal int ukColumnPositionForInference= -1;
173 // default remoting format is Xml
174 private SerializationFormat _remotingFormat = SerializationFormat.Xml;
176 private static int _objectTypeCount; // Bid counter
177 private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
179 /// <devdoc>
180 /// <para>Initializes a new instance of the <see cref='System.Data.DataTable'/> class with no arguments.</para>
181 /// </devdoc>
182 public DataTable() {
183 GC.SuppressFinalize(this);
184 Bid.Trace("<ds.DataTable.DataTable|API> %d#\n", ObjectID);
185 nextRowID = 1;
186 recordManager = new RecordManager(this);
188 _culture = CultureInfo.CurrentCulture;
189 this.columnCollection = new DataColumnCollection(this);
190 this.constraintCollection = new ConstraintCollection(this);
191 this.rowCollection = new DataRowCollection(this);
192 this.indexes = new List<Index>();
194 rowBuilder = new DataRowBuilder(this, -1);
197 /// <devdoc>
198 /// <para>Intitalizes a new instance of the <see cref='System.Data.DataTable'/> class with the specified table
199 /// name.</para>
200 /// </devdoc>
201 public DataTable(string tableName) : this() {
202 this.tableName = tableName == null ? "" : tableName;
205 public DataTable(string tableName, string tableNamespace) : this(tableName) {
206 this.Namespace = tableNamespace;
209 // Deserialize the table from binary/xml stream.
210 protected DataTable(SerializationInfo info, StreamingContext context) : this()
212 bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
213 SerializationFormat remotingFormat = SerializationFormat.Xml;
214 SerializationInfoEnumerator e = info.GetEnumerator();
215 while (e.MoveNext()) {
216 switch(e.Name) {
217 case "DataTable.RemotingFormat" : //DataTable.RemotingFormat does not exist in V1/V1.1 versions
218 remotingFormat = (SerializationFormat)e.Value;
219 break;
223 DeserializeDataTable(info, context, isSingleTable, remotingFormat);
226 [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)]
227 public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
228 SerializationFormat remotingFormat = RemotingFormat;
229 bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
230 SerializeDataTable(info, context, isSingleTable, remotingFormat);
233 // Serialize the table schema and data.
234 private void SerializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
235 info.AddValue("DataTable.RemotingVersion", new Version(2, 0));
237 // SqlHotFix 299, SerializationFormat enumeration types don't exist in V1.1 SP1
238 if (SerializationFormat.Xml != remotingFormat) {
239 info.AddValue("DataTable.RemotingFormat", remotingFormat);
242 if (remotingFormat != SerializationFormat.Xml) {//Binary
243 SerializeTableSchema(info, context, isSingleTable);
244 if (isSingleTable) {
245 SerializeTableData(info, context, 0);
247 } else {//XML/V1.0/V1.1
248 string tempDSNamespace = "";
249 Boolean fCreatedDataSet = false;
251 if (dataSet == null) {
252 DataSet ds = new DataSet("tmpDataSet");
253 // if user set values on DataTable, it isn't necessary
254 // to set them on the DataSet because they won't be inherited
255 // but it is simpler to set them in both places
257 // if user did not set values on DataTable, it is required
258 // to set them on the DataSet so the table will inherit
259 // the value already on the Datatable
260 ds.SetLocaleValue(_culture, _cultureUserSet);
261 ds.CaseSensitive = this.CaseSensitive;
262 ds.namespaceURI = this.Namespace;
263 Debug.Assert(ds.RemotingFormat == SerializationFormat.Xml, "RemotingFormat must be SerializationFormat.Xml");
264 ds.Tables.Add(this);
265 fCreatedDataSet = true;
266 } else {
267 tempDSNamespace = this.DataSet.Namespace;
268 this.DataSet.namespaceURI = this.Namespace; //this.DataSet.Namespace = this.Namespace; ??
271 info.AddValue(KEY_XMLSCHEMA, dataSet.GetXmlSchemaForRemoting(this));
272 info.AddValue(KEY_XMLDIFFGRAM, dataSet.GetRemotingDiffGram(this));
274 if (fCreatedDataSet) {
275 dataSet.Tables.Remove(this);
277 else{
278 dataSet.namespaceURI = tempDSNamespace;
283 // Deserialize the table schema and data.
284 internal void DeserializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
285 if (remotingFormat != SerializationFormat.Xml) {//Binary
286 DeserializeTableSchema(info, context, isSingleTable);
287 if (isSingleTable) {
288 DeserializeTableData(info, context, 0);
289 this.ResetIndexes();
291 } else {//XML/V1.0/V1.1
292 string strSchema = (String)info.GetValue(KEY_XMLSCHEMA, typeof(System.String));
293 string strData = (String)info.GetValue(KEY_XMLDIFFGRAM, typeof(System.String));
295 if (strSchema != null) {
296 DataSet ds = new DataSet();
297 // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
298 ds.ReadXmlSchema(new XmlTextReader( new StringReader( strSchema ) ) );
300 Debug.Assert(ds.Tables.Count == 1, "There should be exactly 1 table here");
301 DataTable table = ds.Tables[0];
302 table.CloneTo(this, null, false);// WebData 111656
303 //this is to avoid the cascading rules in the namespace
304 this.Namespace = table.Namespace;
306 if (strData != null) {
307 ds.Tables.Remove(ds.Tables[0]);
308 ds.Tables.Add(this);
309 ds.ReadXml(new XmlTextReader( new StringReader( strData ) ), XmlReadMode.DiffGram);
310 ds.Tables.Remove(this);
316 // Serialize the columns
317 internal void SerializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
318 //DataTable basic properties
319 info.AddValue("DataTable.TableName", TableName);
320 info.AddValue("DataTable.Namespace", Namespace);
321 info.AddValue("DataTable.Prefix", Prefix);
322 info.AddValue("DataTable.CaseSensitive", _caseSensitive);
323 info.AddValue("DataTable.caseSensitiveAmbient", !_caseSensitiveUserSet);
324 info.AddValue("DataTable.LocaleLCID", Locale.LCID);
325 info.AddValue("DataTable.MinimumCapacity", recordManager.MinimumCapacity);
326 //info.AddValue("DataTable.DisplayExpression", DisplayExpression);
328 //DataTable state internal properties
329 info.AddValue("DataTable.NestedInDataSet", fNestedInDataset);
330 info.AddValue("DataTable.TypeName", TypeName.ToString());
331 info.AddValue("DataTable.RepeatableElement", repeatableElement);
334 //ExtendedProperties
335 info.AddValue("DataTable.ExtendedProperties", ExtendedProperties);
337 //Columns
338 info.AddValue("DataTable.Columns.Count", Columns.Count);
340 //Check for closure of expression in case of single table.
341 if (isSingleTable) {
342 List<DataTable> list = new List<DataTable>();
343 list.Add(this);
344 if (!CheckForClosureOnExpressionTables(list))
345 throw ExceptionBuilder.CanNotRemoteDataTable();
348 IFormatProvider formatProvider = CultureInfo.InvariantCulture;
349 for (int i = 0; i < Columns.Count; i++) {
350 //DataColumn basic properties
351 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i), Columns[i].ColumnName);
352 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i), Columns[i]._columnUri);
353 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i), Columns[i].Prefix);
354 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), Columns[i].ColumnMapping);
355 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i), Columns[i].AllowDBNull);
356 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i), Columns[i].AutoIncrement);
357 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i), Columns[i].AutoIncrementStep);
358 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i), Columns[i].AutoIncrementSeed);
359 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i), Columns[i].Caption);
360 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), Columns[i].DefaultValue);
361 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i), Columns[i].ReadOnly);
362 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i), Columns[i].MaxLength);
363 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), Columns[i].DataType);
365 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), Columns[i].XmlDataType);
366 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), Columns[i].SimpleType);
368 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), Columns[i].DateTimeMode);
370 //DataColumn internal state properties
371 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), Columns[i].AutoIncrementCurrent);
373 //Expression
374 if (isSingleTable) {
375 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i), Columns[i].Expression);
378 //ExtendedProperties
379 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), Columns[i].extendedProperties);
382 //Constraints
383 if (isSingleTable) {
384 SerializeConstraints(info, context, 0, false);
388 // Deserialize all the Columns
389 internal void DeserializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
390 //DataTable basic properties
391 tableName = info.GetString("DataTable.TableName");
392 tableNamespace = info.GetString("DataTable.Namespace");
393 tablePrefix = info.GetString("DataTable.Prefix");
395 bool caseSensitive = info.GetBoolean("DataTable.CaseSensitive");
396 SetCaseSensitiveValue(caseSensitive, true, false);
397 _caseSensitiveUserSet = !info.GetBoolean("DataTable.caseSensitiveAmbient");
399 int lcid = (int)info.GetValue("DataTable.LocaleLCID", typeof(int));
400 CultureInfo culture = new CultureInfo(lcid);
401 SetLocaleValue(culture, true, false);
402 _cultureUserSet = true;
405 MinimumCapacity = info.GetInt32("DataTable.MinimumCapacity");
406 //DisplayExpression = info.GetString("DataTable.DisplayExpression");
408 //DataTable state internal properties
409 fNestedInDataset = (bool) info.GetBoolean("DataTable.NestedInDataSet");
410 string tName = info.GetString("DataTable.TypeName");
411 typeName = new XmlQualifiedName(tName);
412 repeatableElement = info.GetBoolean("DataTable.RepeatableElement");
414 //ExtendedProperties
415 extendedProperties = (PropertyCollection) info.GetValue("DataTable.ExtendedProperties", typeof(PropertyCollection));
417 //Columns
418 int colCount = info.GetInt32("DataTable.Columns.Count");
419 string [] expressions = new string[colCount];
420 Debug.Assert(Columns.Count == 0, "There is column in Table");
422 IFormatProvider formatProvider = CultureInfo.InvariantCulture;
423 for (int i = 0; i < colCount; i++) {
424 DataColumn dc = new DataColumn();
426 //DataColumn public state properties
427 dc.ColumnName = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i));
428 dc._columnUri = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i));
429 dc.Prefix = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i));
431 dc.DataType = (Type) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), typeof(Type));
432 dc.XmlDataType = (string) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), typeof(string));
433 dc.SimpleType = (SimpleType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), typeof(SimpleType));
435 dc.ColumnMapping = (MappingType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), typeof(MappingType));
436 dc.DateTimeMode = (DataSetDateTime) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), typeof(DataSetDateTime));
438 dc.AllowDBNull = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i));
439 dc.AutoIncrement = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i));
440 dc.AutoIncrementStep = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i));
441 dc.AutoIncrementSeed = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i));
442 dc.Caption = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i));
443 dc.DefaultValue = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), typeof(object));
444 dc.ReadOnly = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i));
445 dc.MaxLength= info.GetInt32(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i));
447 //DataColumn internal state properties
448 dc.AutoIncrementCurrent = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), typeof(object));
450 //Expression
451 if (isSingleTable) {
452 expressions[i] = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i));
455 //ExtendedProperties
456 dc.extendedProperties = (PropertyCollection) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), typeof(PropertyCollection));
457 Columns.Add(dc);
459 if (isSingleTable) {
460 for(int i = 0; i < colCount; i++) {
461 if (expressions[i] != null) {
462 Columns[i].Expression = expressions[i];
467 //Constraints
468 if (isSingleTable) {
469 DeserializeConstraints(info, context, /*table index */ 0, /* serialize all constraints */false);// since single table, send table index as 0, meanwhile passing
470 // false for 'allConstraints' means, handle all the constraint related to the table
475 Serialize constraints availabe on the table - note this function is marked internal because it is called by the DataSet deserializer.
476 ***Schema for Serializing ArrayList of Constraints***
477 Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
478 Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
480 internal void SerializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
481 if (allConstraints) {
482 Debug.Assert(DataSet != null);
485 ArrayList constraintList = new ArrayList();
487 for (int i = 0; i < Constraints.Count; i++) {
488 Constraint c = Constraints[i];
490 UniqueConstraint uc = c as UniqueConstraint;
491 if (uc != null) {
492 int[] colInfo = new int[uc.Columns.Length];
493 for (int j = 0; j < colInfo.Length; j++) {
494 colInfo[j] = uc.Columns[j].Ordinal;
497 ArrayList list = new ArrayList();
498 list.Add("U");
499 list.Add(uc.ConstraintName);
500 list.Add(colInfo);
501 list.Add(uc.IsPrimaryKey);
502 list.Add(uc.ExtendedProperties);
504 constraintList.Add(list);
505 } else {
506 ForeignKeyConstraint fk = c as ForeignKeyConstraint;
507 Debug.Assert(fk != null);
508 bool shouldSerialize = (allConstraints == true) || (fk.Table == this && fk.RelatedTable == this);
510 if (shouldSerialize) {
511 int[] parentInfo = new int[fk.RelatedColumns.Length + 1];
512 parentInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.RelatedTable) : 0;
513 for (int j = 1; j < parentInfo.Length; j++) {
514 parentInfo[j] = fk.RelatedColumns[j - 1].Ordinal;
517 int[] childInfo = new int[fk.Columns.Length + 1];
518 childInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.Table) : 0 ; //Since the constraint is on the current table, this is the child table.
519 for (int j = 1; j < childInfo.Length; j++) {
520 childInfo[j] = fk.Columns[j - 1].Ordinal;
523 ArrayList list = new ArrayList();
524 list.Add("F");
525 list.Add(fk.ConstraintName);
526 list.Add(parentInfo);
527 list.Add(childInfo);
528 list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
529 list.Add(fk.ExtendedProperties);
531 constraintList.Add(list);
535 info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), constraintList);
539 Deserialize the constraints on the table.
540 ***Schema for Serializing ArrayList of Constraints***
541 Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
542 Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
544 internal void DeserializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
545 ArrayList constraintList = (ArrayList) info.GetValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), typeof(ArrayList));
547 foreach (ArrayList list in constraintList) {
548 string con = (string) list[0];
550 if (con.Equals("U")) { //Unique Constraints
551 string constraintName = (string) list[1];
553 int[] keyColumnIndexes = (int[]) list[2];
554 bool isPrimaryKey = (bool) list[3];
555 PropertyCollection extendedProperties = (PropertyCollection) list[4];
557 DataColumn[] keyColumns = new DataColumn[keyColumnIndexes.Length];
558 for (int i = 0; i < keyColumnIndexes.Length; i++) {
559 keyColumns[i] = Columns[keyColumnIndexes[i]];
562 //Create the constraint.
563 UniqueConstraint uc = new UniqueConstraint(constraintName, keyColumns, isPrimaryKey);
564 uc.extendedProperties = extendedProperties;
566 //Add the unique constraint and it will in turn set the primary keys also if needed.
567 Constraints.Add(uc);
568 } else { //ForeignKeyConstraints
569 Debug.Assert(con.Equals("F"));
571 string constraintName = (string) list[1];
572 int[] parentInfo = (int[]) list[2];
573 int[] childInfo = (int[]) list[3];
574 int[] rules = (int[]) list[4];
575 PropertyCollection extendedProperties = (PropertyCollection) list[5];
577 //ParentKey Columns.
578 DataTable parentTable = (allConstraints == false) ? this : this.DataSet.Tables[parentInfo[0]];
579 DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
580 for (int i = 0; i < parentkeyColumns.Length; i++) {
581 parentkeyColumns[i] = parentTable.Columns[parentInfo[i + 1]];
584 //ChildKey Columns.
585 DataTable childTable = (allConstraints == false) ? this : this.DataSet.Tables[childInfo[0]];
586 DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
587 for (int i = 0; i < childkeyColumns.Length; i++) {
588 childkeyColumns[i] = childTable.Columns[childInfo[i + 1]];
591 //Create the Constraint.
592 ForeignKeyConstraint fk = new ForeignKeyConstraint(constraintName, parentkeyColumns, childkeyColumns);
593 fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
594 fk.UpdateRule = (Rule) rules[1];
595 fk.DeleteRule = (Rule) rules[2];
596 fk.extendedProperties = extendedProperties;
598 //Add just the foreign key constraint without creating unique constraint.
599 Constraints.Add(fk, false);
604 // Serialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
605 internal void SerializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
606 int colCount = Columns.Count;
607 for (int i = 0; i < colCount; i++) {
608 info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i), Columns[i].Expression);
612 // Deserialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
613 internal void DeserializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
614 int colCount = Columns.Count;
615 for (int i = 0; i < colCount; i++) {
616 string expr = info.GetString(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i));
617 if (0 != expr.Length) {
618 Columns[i].Expression = expr;
623 // Serialize all the Rows.
624 internal void SerializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
625 //Cache all the column count, row count
626 int colCount = Columns.Count;
627 int rowCount = Rows.Count;
628 int modifiedRowCount = 0;
629 int editRowCount = 0;
631 //Compute row states and assign the bits accordingly - 00[Unchanged], 01[Added], 10[Modifed], 11[Deleted]
632 BitArray rowStates = new BitArray(rowCount * 3, false); //All bit flags are set to false on initialization of the BitArray.
633 for (int i = 0; i < rowCount; i++) {
634 int bitIndex = i * 3;
635 DataRow row = Rows[i];
636 DataRowState rowState = row.RowState;
637 switch (rowState) {
638 case DataRowState.Unchanged:
639 //rowStates[bitIndex] = false;
640 //rowStates[bitIndex + 1] = false;
641 break;
642 case DataRowState.Added:
643 //rowStates[bitIndex] = false;
644 rowStates[bitIndex + 1] = true;
645 break;
646 case DataRowState.Modified:
647 rowStates[bitIndex] = true;
648 //rowStates[bitIndex + 1] = false;
649 modifiedRowCount++;
650 break;
651 case DataRowState.Deleted:
652 rowStates[bitIndex] = true;
653 rowStates[bitIndex + 1] = true;
654 break;
655 default:
656 throw ExceptionBuilder.InvalidRowState(rowState);
658 if (-1 != row.tempRecord) {
659 rowStates[bitIndex + 2] = true;
660 editRowCount++;
664 //Compute the actual storage records that need to be created.
665 int recordCount = rowCount + modifiedRowCount + editRowCount;
667 //Create column storages.
668 ArrayList storeList = new ArrayList();
669 ArrayList nullbitList = new ArrayList();
670 if (recordCount > 0) { //Create the storage only if have records.
671 for (int i = 0; i < colCount; i++) {
672 object store = Columns[i].GetEmptyColumnStore(recordCount);
673 storeList.Add(store);
674 BitArray nullbits = new BitArray(recordCount);
675 nullbitList.Add(nullbits);
679 //Copy values into column storages
680 int recordsConsumed = 0;
681 Hashtable rowErrors = new Hashtable();
682 Hashtable colErrors = new Hashtable();
683 for (int i = 0; i < rowCount; i++) {
684 int recordsPerRow = Rows[i].CopyValuesIntoStore(storeList, nullbitList, recordsConsumed);
685 GetRowAndColumnErrors(i, rowErrors, colErrors);
686 recordsConsumed += recordsPerRow;
689 IFormatProvider formatProvider = CultureInfo.InvariantCulture;
690 //Serialize all the computed values.
691 info.AddValue(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex), rowCount);
692 info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex), recordCount);
693 info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), rowStates);
694 info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), storeList);
695 info.AddValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), nullbitList);
696 info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), rowErrors);
697 info.AddValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), colErrors);
700 // Deserialize all the Rows.
701 internal void DeserializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
702 bool enforceConstraintsOrg = enforceConstraints;
703 bool inDataLoadOrg = inDataLoad;
706 try {
707 enforceConstraints = false;
708 inDataLoad = true;
709 IFormatProvider formatProvider = CultureInfo.InvariantCulture;
710 int rowCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex));
711 int recordCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex));
712 BitArray rowStates = (BitArray) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), typeof(BitArray));
713 ArrayList storeList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), typeof(ArrayList));
714 ArrayList nullbitList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), typeof(ArrayList));
715 Hashtable rowErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), typeof(Hashtable));
716 rowErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
717 Hashtable colErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), typeof(Hashtable));
718 colErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
721 if (recordCount <= 0) { //No need for deserialization of the storage and errors if there are no records.
722 return;
725 //Point the record manager storage to the deserialized values.
726 for (int i = 0; i < Columns.Count; i++) {
727 Columns[i].SetStorage(storeList[i], (BitArray) nullbitList[i]);
730 //Create rows and set the records appropriately.
731 int recordIndex = 0;
732 DataRow[] rowArr = new DataRow[recordCount];
733 for (int i = 0; i < rowCount; i++) {
734 //Create a new row which sets old and new records to -1.
735 DataRow row = NewEmptyRow();
736 rowArr[recordIndex] = row;
737 int bitIndex = i * 3;
738 switch (ConvertToRowState(rowStates, bitIndex)) {
739 case DataRowState.Unchanged:
740 row.oldRecord = recordIndex;
741 row.newRecord = recordIndex;
742 recordIndex += 1;
743 break;
744 case DataRowState.Added:
745 row.oldRecord = -1;
746 row.newRecord = recordIndex;
747 recordIndex += 1;
748 break;
749 case DataRowState.Modified:
750 row.oldRecord = recordIndex;
751 row.newRecord = recordIndex + 1;
752 rowArr[recordIndex + 1] = row;
753 recordIndex += 2;
754 break;
755 case DataRowState.Deleted:
756 row.oldRecord = recordIndex;
757 row.newRecord = -1;
758 recordIndex += 1;
759 break;
761 if (rowStates[bitIndex + 2]) {
762 row.tempRecord = recordIndex;
763 rowArr[recordIndex] = row;
764 recordIndex += 1;
765 } else {
766 row.tempRecord = -1;
768 Rows.ArrayAdd(row);
769 row.rowID = nextRowID;
770 nextRowID++;
771 ConvertToRowError(i, rowErrors, colErrors);
773 recordManager.SetRowCache(rowArr);
774 ResetIndexes();
775 } finally {
776 enforceConstraints = enforceConstraintsOrg;
777 inDataLoad = inDataLoadOrg;
781 // Constructs the RowState from the two bits in the bitarray.
782 private DataRowState ConvertToRowState(BitArray bitStates, int bitIndex) {
783 Debug.Assert(bitStates != null);
784 Debug.Assert(bitStates.Length > bitIndex);
786 bool b1 = bitStates[bitIndex];
787 bool b2 = bitStates[bitIndex + 1];
789 if (!b1 && !b2) {
790 return DataRowState.Unchanged;
791 } else if (!b1 && b2) {
792 return DataRowState.Added;
793 } else if (b1 && !b2) {
794 return DataRowState.Modified;
795 } else if (b1 && b2) {
796 return DataRowState.Deleted;
797 } else {
798 throw ExceptionBuilder.InvalidRowBitPattern();
802 // Get the error on the row and columns - Marked internal so that DataSet deserializer can call into this
803 internal void GetRowAndColumnErrors(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
804 Debug.Assert(Rows.Count > rowIndex);
805 Debug.Assert(rowErrors != null);
806 Debug.Assert(colErrors != null);
808 DataRow row = Rows[rowIndex];
810 if (row.HasErrors) {
811 rowErrors.Add(rowIndex, row.RowError);
812 DataColumn[] dcArr = row.GetColumnsInError();
813 if (dcArr.Length > 0) {
814 int[] columnsInError = new int[dcArr.Length];
815 string[] columnErrors = new string[dcArr.Length];
816 for (int i = 0; i < dcArr.Length; i++) {
817 columnsInError[i] = dcArr[i].Ordinal;
818 columnErrors[i] = row.GetColumnError(dcArr[i]);
820 ArrayList list = new ArrayList();
821 list.Add(columnsInError);
822 list.Add(columnErrors);
823 colErrors.Add(rowIndex, list);
828 // Set the row and columns in error..
829 private void ConvertToRowError(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
830 Debug.Assert(Rows.Count > rowIndex);
831 Debug.Assert(rowErrors != null);
832 Debug.Assert(colErrors != null);
834 DataRow row = Rows[rowIndex];
836 if (rowErrors.ContainsKey(rowIndex)) {
837 row.RowError = (string) rowErrors[rowIndex];
839 if (colErrors.ContainsKey(rowIndex)) {
840 ArrayList list = (ArrayList) colErrors[rowIndex];
841 int[] columnsInError = (int[]) list[0];
842 string[] columnErrors = (string[]) list[1];
843 Debug.Assert(columnsInError.Length == columnErrors.Length);
844 for (int i = 0; i < columnsInError.Length; i++) {
845 row.SetColumnError(columnsInError[i], columnErrors[i]);
850 /// <devdoc>
851 /// <para>Indicates whether string comparisons within the table are case-sensitive.</para>
852 /// </devdoc>
853 [ResDescriptionAttribute(Res.DataTableCaseSensitiveDescr)]
854 public bool CaseSensitive {
855 get {
856 //The following assert is valid except when calling DataSet.set_CaseSensitive which Validates constraints and failing here
857 //Debug.Assert(_caseSensitiveUserSet || (null == dataSet) || (dataSet.CaseSensitive == _caseSensitive), "CaseSensitive mismatch");
858 return _caseSensitive;
860 set {
861 if (_caseSensitive != value) {
862 bool oldValue = _caseSensitive;
863 bool oldUserSet = _caseSensitiveUserSet;
864 _caseSensitive = value;
865 _caseSensitiveUserSet = true;
867 if (DataSet != null && !DataSet.ValidateCaseConstraint()) {
868 _caseSensitive = oldValue;
869 _caseSensitiveUserSet = oldUserSet;
870 throw ExceptionBuilder.CannotChangeCaseLocale();
872 SetCaseSensitiveValue(value, true, true);
874 _caseSensitiveUserSet = true;
878 internal bool AreIndexEventsSuspended {
879 get { return (0 < _suspendIndexEvents); }
882 internal void RestoreIndexEvents(bool forceReset) {
883 Bid.Trace("<ds.DataTable.RestoreIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
884 if (0 < _suspendIndexEvents) {
885 _suspendIndexEvents--;
886 if (0 == _suspendIndexEvents) {
887 Exception first = null;
888 SetShadowIndexes();
889 try{
890 // the length of shadowIndexes will not change
891 // but the array instance may change during
892 // events during Index.Reset
893 int numIndexes = shadowIndexes.Count;
894 for (int i = 0; i < numIndexes; i++) {
895 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
896 try {
897 if (forceReset || ndx.HasRemoteAggregate) {
898 ndx.Reset(); // resets & fires
900 else {
901 ndx.FireResetEvent(); // fire the Reset event we were firing
904 catch(Exception e) {
905 if (!ADP.IsCatchableExceptionType (e)) {
906 throw;
908 ExceptionBuilder.TraceExceptionWithoutRethrow(e);
909 if (null == first) {
910 first = e;
914 if (null != first) {
915 throw first;
918 finally {
919 RestoreShadowIndexes();
925 internal void SuspendIndexEvents() {
926 Bid.Trace("<ds.DataTable.SuspendIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
927 _suspendIndexEvents++;
930 [Browsable(false)]
931 public bool IsInitialized {
932 get {
933 return !fInitInProgress;
937 private bool IsTypedDataTable {
938 get {
939 switch (_isTypedDataTable) {
940 case 0:
941 _isTypedDataTable = (byte)((this.GetType() != typeof(DataTable))? 1 : 2);
942 return (1 == _isTypedDataTable);
943 case 1:
944 return true;
945 default:
946 return false;
951 internal bool SetCaseSensitiveValue(bool isCaseSensitive, bool userSet, bool resetIndexes) {
952 if (userSet || (!_caseSensitiveUserSet && (_caseSensitive != isCaseSensitive))) {
953 _caseSensitive = isCaseSensitive;
954 if (isCaseSensitive) {
955 _compareFlags = CompareOptions.None;
957 else {
958 _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
960 if (resetIndexes) {
961 ResetIndexes();
962 foreach (Constraint constraint in Constraints) {
963 constraint.CheckConstraint();
966 return true;
968 return false;
972 private void ResetCaseSensitive() {
973 // this method is used design-time scenarios via reflection
974 // by the property grid context menu to show the Reset option or not
975 SetCaseSensitiveValue((null != dataSet) && dataSet.CaseSensitive, true, true);
976 _caseSensitiveUserSet = false;
979 internal bool ShouldSerializeCaseSensitive() {
980 // this method is used design-time scenarios via reflection
981 // by the property grid to show the CaseSensitive property in bold or not
982 // by the code dom for persisting the CaseSensitive property or not
983 return _caseSensitiveUserSet;
986 internal bool SelfNested {
987 get {
988 // Is this correct? if ((top[i].nestedParentRelation!= null) && (top[i].nestedParentRelation.ParentTable == top[i]))
989 foreach(DataRelation rel in ParentRelations) {
990 if (rel.Nested && rel.ParentTable == this) {
991 return true;
994 return false;
997 /* internal bool SelfNestedWithOneRelation {
998 get {
999 return (this.ParentRelations.Count == 1 && (this.ParentRelations[0].ParentTable == this));
1004 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
1005 internal List<Index> LiveIndexes {
1006 get {
1007 if (!AreIndexEventsSuspended) {
1008 for (int i = indexes.Count-1; 0 <= i; --i) {
1009 Index index = indexes[i];
1010 if (index.RefCount <= 1) {
1011 index.RemoveRef();
1015 return indexes;
1020 DefaultValue(SerializationFormat.Xml)
1022 public SerializationFormat RemotingFormat {
1023 get {
1024 return _remotingFormat;
1026 set {
1027 if (value != SerializationFormat.Binary && value != SerializationFormat.Xml) {
1028 throw ExceptionBuilder.InvalidRemotingFormat(value);
1030 // table can not have different format than its dataset, unless it is stand alone datatable
1031 if (this.DataSet != null && value != this.DataSet.RemotingFormat) {
1032 throw ExceptionBuilder.CanNotSetRemotingFormat();
1034 _remotingFormat = value;
1038 // used to keep temporary state of unique Key posiotion to be added for inference only
1039 internal int UKColumnPositionForInference {
1040 get {
1041 return ukColumnPositionForInference;
1043 set{
1044 ukColumnPositionForInference= value;
1048 /// <devdoc>
1049 /// <para>Gets the collection of child relations for this <see cref='System.Data.DataTable'/>.</para>
1050 /// </devdoc>
1052 Browsable(false),
1053 ResDescriptionAttribute(Res.DataTableChildRelationsDescr),
1054 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
1056 public DataRelationCollection ChildRelations {
1057 get {
1058 if (childRelationsCollection == null)
1059 childRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, false);
1060 return childRelationsCollection;
1064 /// <devdoc>
1065 /// <para>Gets the collection of columns that belong to this table.</para>
1066 /// </devdoc>
1068 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1069 ResCategoryAttribute(Res.DataCategory_Data),
1070 ResDescriptionAttribute(Res.DataTableColumnsDescr)
1072 public DataColumnCollection Columns {
1073 get {
1074 return columnCollection;
1078 private void ResetColumns() {
1079 // this method is used design-time scenarios via reflection
1080 // by the property grid context menu to show the Reset option or not
1081 Columns.Clear();
1084 private CompareInfo CompareInfo {
1085 get {
1086 if (null == _compareInfo) {
1087 _compareInfo = Locale.CompareInfo;
1089 return _compareInfo;
1093 /// <devdoc>
1094 /// <para>Gets the collection of constraints maintained by this table.</para>
1095 /// </devdoc>
1097 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1098 ResCategoryAttribute(Res.DataCategory_Data),
1099 ResDescriptionAttribute(Res.DataTableConstraintsDescr)
1101 public ConstraintCollection Constraints {
1102 get {
1103 return constraintCollection;
1107 /// <devdoc>
1108 /// <para>
1109 /// Resets the <see cref='System.Data.DataTable.Constraints'/> property to its default state.
1110 /// </para>
1111 /// </devdoc>
1112 private void ResetConstraints() {
1113 Constraints.Clear();
1116 /// <devdoc>
1117 /// <para>Gets the <see cref='System.Data.DataSet'/> that this table belongs to.</para>
1118 /// </devdoc>
1119 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false), ResDescriptionAttribute(Res.DataTableDataSetDescr)]
1120 public DataSet DataSet {
1121 get {
1122 return dataSet;
1126 /// <devdoc>
1127 /// Internal method for setting the DataSet pointer.
1128 /// </devdoc>
1129 internal void SetDataSet(DataSet dataSet) {
1130 if (this.dataSet != dataSet) {
1131 this.dataSet = dataSet;
1133 // Inform all the columns of the dataset being set.
1134 DataColumnCollection cols = Columns;
1135 for (int i = 0; i < cols.Count; i++)
1136 cols[i].OnSetDataSet();
1138 if (this.DataSet != null) {
1139 defaultView = null;
1141 //Set the remoting format variable directly
1142 if (dataSet != null) {
1143 _remotingFormat = dataSet.RemotingFormat;
1148 /// <devdoc>
1149 /// <para>Gets a customized view of the table which may include a
1150 /// filtered view, or a cursor position.</para>
1151 /// </devdoc>
1152 [Browsable(false), ResDescriptionAttribute(Res.DataTableDefaultViewDescr)]
1153 public DataView DefaultView {
1154 get {
1155 DataView view = defaultView;
1156 if (null == view) {
1157 if (null != dataSet) {
1158 view = dataSet.DefaultViewManager.CreateDataView(this);
1160 else {
1161 view = new DataView(this, true);
1162 view.SetIndex2("", DataViewRowState.CurrentRows, null, true);
1164 // avoid HostProtectionAttribute(Synchronization=true) by not calling virtual methods from inside a lock
1165 view = Interlocked.CompareExchange<DataView>(ref defaultView, view, null);
1166 if (null == view) {
1167 view = defaultView;
1170 return view;
1174 /// <devdoc>
1175 /// <para>Gets or sets the expression that will return a value used to represent
1176 /// this table in UI.</para>
1177 /// </devdoc>
1179 DefaultValue(""),
1180 ResCategoryAttribute(Res.DataCategory_Data),
1181 ResDescriptionAttribute(Res.DataTableDisplayExpressionDescr)
1183 public string DisplayExpression {
1184 get {
1185 return DisplayExpressionInternal;
1187 set {
1188 if (value != null && value.Length > 0) {
1189 this.displayExpression = new DataExpression(this, value);
1191 else {
1192 this.displayExpression = null;
1196 internal string DisplayExpressionInternal {
1197 get {
1198 return(displayExpression != null ? displayExpression.Expression : "");
1202 internal bool EnforceConstraints {
1203 get {
1204 if (SuspendEnforceConstraints) {
1205 return false;
1207 if (dataSet != null)
1208 return dataSet.EnforceConstraints;
1210 return this.enforceConstraints;
1212 set {
1213 if (dataSet == null && this.enforceConstraints != value) {
1214 if (value)
1215 EnableConstraints();
1217 this.enforceConstraints = value;
1222 internal bool SuspendEnforceConstraints {
1223 get {
1224 return _suspendEnforceConstraints ;
1226 set {
1227 _suspendEnforceConstraints = value;
1231 internal void EnableConstraints()
1233 bool errors = false;
1234 foreach (Constraint constr in Constraints)
1236 if (constr is UniqueConstraint)
1237 errors |= constr.IsConstraintViolated();
1240 foreach (DataColumn column in Columns) {
1241 if (!column.AllowDBNull) {
1242 errors |= column.IsNotAllowDBNullViolated();
1244 if (column.MaxLength >= 0) {
1245 errors |= column.IsMaxLengthViolated();
1249 if (errors) {
1250 this.EnforceConstraints = false;
1251 throw ExceptionBuilder.EnforceConstraint();
1255 /// <devdoc>
1256 /// <para>Gets the collection of customized user information.</para>
1257 /// </devdoc>
1259 ResCategoryAttribute(Res.DataCategory_Data),
1260 Browsable(false),
1261 ResDescriptionAttribute(Res.ExtendedPropertiesDescr)
1263 public PropertyCollection ExtendedProperties {
1264 get {
1265 if (extendedProperties == null) {
1266 extendedProperties = new PropertyCollection();
1268 return extendedProperties;
1272 internal IFormatProvider FormatProvider {
1273 get {
1274 // used for Formating/Parsing
1275 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemglobalizationcultureinfoclassisneutralculturetopic.asp
1276 if (null == _formatProvider) {
1277 CultureInfo culture = Locale;
1278 if (culture.IsNeutralCulture) {
1279 culture = CultureInfo.InvariantCulture;
1281 _formatProvider = (IFormatProvider)culture;
1283 return _formatProvider;
1287 /// <devdoc>
1288 /// <para>Gets a value indicating whether there are errors in any of the rows in any of
1289 /// the tables of the <see cref='System.Data.DataSet'/> to which the table belongs.</para>
1290 /// </devdoc>
1291 [Browsable(false), ResDescriptionAttribute(Res.DataTableHasErrorsDescr)]
1292 public bool HasErrors {
1293 get {
1294 for (int i = 0; i < Rows.Count; i++) {
1295 if (Rows[i].HasErrors) {
1296 return true;
1299 return false;
1303 /// <devdoc>
1304 /// <para>Gets or sets the locale information used to compare strings within the table.</para>
1305 /// <para>Also used for locale sensitive, case,kana,width insensitive column name lookups</para>
1306 /// <para>Also used for converting values to and from string</para>
1307 /// </devdoc>
1308 [ResDescriptionAttribute(Res.DataTableLocaleDescr)]
1309 public CultureInfo Locale {
1310 get {
1311 // used for Comparing not Formatting/Parsing
1312 Debug.Assert(null != _culture, "null culture");
1313 Debug.Assert(_cultureUserSet || (null == dataSet) || _culture.Equals(dataSet.Locale), "Locale mismatch");
1314 return _culture;
1316 set {
1317 IntPtr hscp;
1318 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Locale|API> %d#\n", ObjectID);
1319 try {
1320 bool userSet = true;
1321 if (null == value) {
1322 // reset Locale to inherit from DataSet
1323 userSet = false;
1324 value = (null != dataSet) ? dataSet.Locale : _culture;
1326 if (_culture != value && !_culture.Equals(value)) {
1327 bool flag = false;
1328 bool exceptionThrown = false;
1329 CultureInfo oldLocale = _culture;
1330 bool oldUserSet = _cultureUserSet;
1331 try {
1332 _cultureUserSet = true;
1333 SetLocaleValue(value, true, false);
1334 if ((null == DataSet) || DataSet.ValidateLocaleConstraint()) {
1335 flag = false;
1336 SetLocaleValue(value, true, true);
1337 flag = true;
1340 catch {
1341 exceptionThrown = true;
1342 throw;
1344 finally {
1345 if (!flag) { // reset old locale if ValidationFailed or exception thrown
1346 try {
1347 SetLocaleValue(oldLocale, true, true);
1349 catch(Exception e) { // failed to reset all indexes for all constraints
1350 if (!Common.ADP.IsCatchableExceptionType(e)) {
1351 throw;
1353 Common.ADP.TraceExceptionWithoutRethrow(e);
1355 _cultureUserSet = oldUserSet;
1356 if (!exceptionThrown) {
1357 throw ExceptionBuilder.CannotChangeCaseLocale(null);
1361 SetLocaleValue(value, true, true);
1363 _cultureUserSet = userSet;
1365 finally{
1366 Bid.ScopeLeave(ref hscp);
1371 internal bool SetLocaleValue(CultureInfo culture, bool userSet, bool resetIndexes) {
1372 Debug.Assert(null != culture, "SetLocaleValue: no locale");
1373 if (userSet || resetIndexes || (!_cultureUserSet && !_culture.Equals(culture))) {
1374 _culture = culture;
1375 _compareInfo = null;
1376 _formatProvider = null;
1377 _hashCodeProvider = null;
1379 foreach(DataColumn column in Columns) {
1380 column._hashCode = GetSpecialHashCode(column.ColumnName);
1382 if (resetIndexes) {
1383 ResetIndexes();
1384 foreach (Constraint constraint in Constraints) {
1385 constraint.CheckConstraint();
1388 return true;
1390 return false;
1393 internal bool ShouldSerializeLocale() {
1394 // this method is used design-time scenarios via reflection
1395 // by the property grid to show the Locale property in bold or not
1396 // by the code dom for persisting the Locale property or not
1398 // we always want the locale persisted if set by user or different the current thread if standalone table
1399 // but that logic should by performed by the serializion code
1400 return _cultureUserSet;
1403 /// <devdoc>
1404 /// <para>Gets or sets the initial starting size for this table.</para>
1405 /// </devdoc>
1407 DefaultValue(50),
1408 ResCategoryAttribute(Res.DataCategory_Data),
1409 ResDescriptionAttribute(Res.DataTableMinimumCapacityDescr)
1411 public int MinimumCapacity {
1412 get {
1413 return recordManager.MinimumCapacity;
1415 set {
1416 if (value != recordManager.MinimumCapacity) {
1417 recordManager.MinimumCapacity = value;
1422 internal int RecordCapacity {
1423 get {
1424 return recordManager.RecordCapacity;
1429 internal int ElementColumnCount {
1430 get {
1431 return elementColumnCount;
1433 set {
1434 if ((value > 0) && (xmlText != null))
1435 throw ExceptionBuilder.TableCannotAddToSimpleContent();
1436 else elementColumnCount = value;
1440 /// <devdoc>
1441 /// <para>Gets the collection of parent relations for this <see cref='System.Data.DataTable'/>.</para>
1442 /// </devdoc>
1444 Browsable(false),
1445 ResDescriptionAttribute(Res.DataTableParentRelationsDescr),
1446 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
1448 public DataRelationCollection ParentRelations {
1449 get {
1450 if (parentRelationsCollection == null)
1451 parentRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, true);
1452 return parentRelationsCollection;
1456 internal bool MergingData {
1457 get {
1458 return mergingData;
1460 set {
1461 mergingData = value;
1465 internal DataRelation[] NestedParentRelations {
1466 get {
1467 #if DEBUG
1468 DataRelation[] nRel = FindNestedParentRelations();
1469 Debug.Assert(nRel.Length == _nestedParentRelations.Length, "nestedParent cache is broken");
1470 for(int i = 0; i < nRel.Length; i++) {
1471 Debug.Assert(null != nRel[i], "null relation");
1472 Debug.Assert(null != _nestedParentRelations[i], "null relation");
1473 Debug.Assert(nRel[i] == _nestedParentRelations[i], "unequal relations");
1475 #endif
1476 return _nestedParentRelations;
1480 internal bool SchemaLoading {
1481 get {
1482 return schemaLoading;
1487 internal void CacheNestedParent() {
1488 _nestedParentRelations = FindNestedParentRelations();
1491 private DataRelation[] FindNestedParentRelations() {
1492 List<DataRelation> nestedParents = null;
1493 foreach(DataRelation relation in this.ParentRelations) {
1494 if(relation.Nested) {
1495 if (null == nestedParents) {
1496 nestedParents = new List<DataRelation>();
1498 nestedParents.Add(relation);
1501 if ((null == nestedParents) || (nestedParents.Count == 0)) {
1502 return EmptyArrayDataRelation;
1504 return nestedParents.ToArray();
1508 internal int NestedParentsCount {
1509 get {
1510 int count = 0;
1511 foreach(DataRelation relation in this.ParentRelations) {
1512 if(relation.Nested)
1513 count++;
1515 return count;
1519 /// <devdoc>
1520 /// <para>Gets or sets an array of columns that function as primary keys for the data
1521 /// table.</para>
1522 /// </devdoc>
1524 TypeConverter(typeof(PrimaryKeyTypeConverter)),
1525 ResDescriptionAttribute(Res.DataTablePrimaryKeyDescr),
1526 ResCategoryAttribute(Res.DataCategory_Data),
1527 Editor("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing)
1529 public DataColumn[] PrimaryKey {
1530 get {
1531 UniqueConstraint primayKeyConstraint = primaryKey;
1532 if (null != primayKeyConstraint) {
1533 Debug.Assert(2 <= primaryKey.ConstraintIndex.RefCount, "bad primaryKey index RefCount");
1534 return primayKeyConstraint.Key.ToArray();
1536 return zeroColumns;
1538 set {
1539 UniqueConstraint key = null;
1540 UniqueConstraint existingKey = null;
1542 // Loading with persisted property
1543 if (fInitInProgress && value != null) {
1544 delayedSetPrimaryKey = value;
1545 return;
1548 if ((value != null) && (value.Length != 0)) {
1549 int count = 0;
1550 for (int i = 0; i < value.Length; i++) {
1551 if (value[i] != null)
1552 count++;
1553 else
1554 break;
1557 if (count != 0) {
1558 DataColumn[] newValue = value;
1559 if (count != value.Length) {
1560 newValue = new DataColumn[count];
1561 for (int i = 0; i < count; i++)
1562 newValue[i] = value[i];
1564 key = new UniqueConstraint(newValue);
1565 if (key.Table != this)
1566 throw ExceptionBuilder.TableForeignPrimaryKey();
1570 if (key == primaryKey || (key != null && key.Equals(primaryKey)))
1571 return;
1573 // Use an existing UniqueConstraint that matches if one exists
1574 if ((existingKey = (UniqueConstraint)Constraints.FindConstraint(key)) != null) {
1575 key.ColumnsReference.CopyTo(existingKey.Key.ColumnsReference, 0);
1576 key = existingKey;
1579 UniqueConstraint oldKey = primaryKey;
1580 primaryKey = null;
1581 if (oldKey != null) {
1582 oldKey.ConstraintIndex.RemoveRef();
1584 // SQLBU 429176: if PrimaryKey is removed, reset LoadDataRow indexes
1585 if (null != loadIndex) {
1586 loadIndex.RemoveRef();
1587 loadIndex = null;
1589 if (null != loadIndexwithOriginalAdded) {
1590 loadIndexwithOriginalAdded.RemoveRef();
1591 loadIndexwithOriginalAdded = null;
1593 if (null != loadIndexwithCurrentDeleted) {
1594 loadIndexwithCurrentDeleted.RemoveRef();
1595 loadIndexwithCurrentDeleted = null;
1597 Constraints.Remove(oldKey);
1600 // Add the key if there isnt an existing matching key in collection
1601 if (key != null && existingKey == null)
1602 Constraints.Add(key);
1604 primaryKey = key;
1606 Debug.Assert(Constraints.FindConstraint(primaryKey) == primaryKey, "PrimaryKey is not in ConstraintCollection");
1607 _primaryIndex = (key != null) ? key.Key.GetIndexDesc() : zeroIndexField;
1609 if (primaryKey != null) {
1610 // must set index for DataView.Sort before setting AllowDBNull which can fail
1611 key.ConstraintIndex.AddRef();
1613 for (int i = 0; i < key.ColumnsReference.Length; i++)
1614 key.ColumnsReference[i].AllowDBNull = false;
1619 /// <devdoc>
1620 /// <para>
1621 /// Indicates whether the <see cref='System.Data.DataTable.PrimaryKey'/> property should be persisted.
1622 /// </para>
1623 /// </devdoc>
1624 private bool ShouldSerializePrimaryKey() {
1625 return(primaryKey != null);
1628 /// <devdoc>
1629 /// <para>
1630 /// Resets the <see cref='System.Data.DataTable.PrimaryKey'/> property to its default state.
1631 /// </para>
1632 /// </devdoc>
1633 private void ResetPrimaryKey() {
1634 PrimaryKey = null;
1637 /// <devdoc>
1638 /// <para>Gets the collection of rows that belong to this table.</para>
1639 /// </devdoc>
1640 [Browsable(false), ResDescriptionAttribute(Res.DataTableRowsDescr)]
1641 public DataRowCollection Rows {
1642 get {
1643 return rowCollection;
1647 /// <devdoc>
1648 /// <para>Gets or sets the name of the table.</para>
1649 /// </devdoc>
1651 RefreshProperties(RefreshProperties.All),
1652 DefaultValue(""),
1653 ResCategoryAttribute(Res.DataCategory_Data),
1654 ResDescriptionAttribute(Res.DataTableTableNameDescr)
1656 public string TableName {
1657 get {
1658 return tableName;
1660 set {
1661 IntPtr hscp;
1662 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_TableName|API> %d#, value='%ls'\n", ObjectID, value);
1663 try {
1664 if (value == null) {
1665 value = "";
1667 CultureInfo currentLocale = this.Locale;
1668 if (String.Compare(tableName, value, true, currentLocale) != 0) {
1669 if (dataSet != null) {
1670 if (value.Length == 0)
1671 throw ExceptionBuilder.NoTableName();
1672 if ((0 == String.Compare(value, dataSet.DataSetName, true, dataSet.Locale)) && !fNestedInDataset)
1673 throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName);
1675 DataRelation[] nestedRelations = NestedParentRelations;
1676 if (nestedRelations.Length == 0) {
1677 dataSet.Tables.RegisterName(value, this.Namespace);
1679 else {
1680 foreach(DataRelation rel in nestedRelations) {
1681 if (!rel.ParentTable.Columns.CanRegisterName(value)) {
1682 throw ExceptionBuilder.CannotAddDuplicate2(value);
1685 // if it cannot register the following line will throw exception
1686 dataSet.Tables.RegisterName(value, this.Namespace);
1688 foreach(DataRelation rel in nestedRelations) {
1689 rel.ParentTable.Columns.RegisterColumnName(value, null);
1690 rel.ParentTable.Columns.UnregisterName(this.TableName);
1694 if (tableName.Length != 0) {
1695 dataSet.Tables.UnregisterName(tableName);
1698 RaisePropertyChanging("TableName");
1699 tableName = value;
1700 encodedTableName = null;
1702 else if (String.Compare(tableName, value, false, currentLocale) != 0) {
1703 RaisePropertyChanging("TableName");
1704 tableName = value;
1705 encodedTableName = null;
1708 finally {
1709 Bid.ScopeLeave(ref hscp);
1715 internal string EncodedTableName {
1716 get {
1717 string encodedTblName = this.encodedTableName;
1718 if (null == encodedTblName) {
1719 encodedTblName = XmlConvert.EncodeLocalName( this.TableName );
1720 this.encodedTableName = encodedTblName;
1722 return encodedTblName;
1725 private string GetInheritedNamespace(List<DataTable> visitedTables){
1726 // if there is nested relation: ie: this table is nested child of a another table and
1727 // if it is not self nested, return parent tables NS: Meanwhile make sure SQLBUDT 240219 is FIXED
1728 DataRelation[] nestedRelations = NestedParentRelations;
1729 if (nestedRelations.Length > 0) {
1730 for(int i =0; i < nestedRelations.Length; i++) {
1731 DataRelation rel = nestedRelations[i];
1732 if (rel.ParentTable.tableNamespace != null) {
1733 return rel.ParentTable.tableNamespace; // if parent table has a non-null NS, return it
1736 // Assumption, in hierarchy of multiple nested relation, a child table with no NS, has DataRelation
1737 // only and only with parent DataTable witin the same namespace
1738 int j = 0;
1739 while(j < nestedRelations.Length &&((nestedRelations[j].ParentTable == this)||(visitedTables.Contains(nestedRelations[j].ParentTable)))) {
1740 j++;
1742 if (j < nestedRelations.Length) {
1743 DataTable parentTable = nestedRelations[j].ParentTable;
1744 if (!visitedTables.Contains(parentTable))
1745 visitedTables.Add(parentTable);
1746 return parentTable.GetInheritedNamespace(visitedTables);// this is the same as return parentTable.Namespace
1748 } // dont put else
1749 if (DataSet != null) { // if it cant return from parent tables, return NS from dataset, if exists
1750 return DataSet.Namespace;
1752 else {
1753 return string.Empty;
1758 /// <devdoc>
1759 /// <para>
1760 /// Gets or sets the namespace for the <see cref='System.Data.DataTable'/>.
1761 /// </para>
1762 /// </devdoc>
1764 ResCategoryAttribute(Res.DataCategory_Data),
1765 ResDescriptionAttribute(Res.DataTableNamespaceDescr)
1767 public string Namespace {
1768 get {
1769 if (tableNamespace == null) {
1770 return GetInheritedNamespace(new List<DataTable>());
1772 return tableNamespace;
1774 set {
1775 IntPtr hscp;
1776 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Namespace|API> %d#, value='%ls'\n", ObjectID, value);
1777 try {
1778 if(value != tableNamespace) {
1779 if (dataSet != null) {
1780 string realNamespace = (value == null ? GetInheritedNamespace(new List<DataTable>()) : value);
1781 if (realNamespace != Namespace) {
1782 // do this extra check only if the namespace is really going to change
1783 // inheritance-wise.
1784 if (dataSet.Tables.Contains( this.TableName, realNamespace, true, true))
1785 throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
1787 CheckCascadingNamespaceConflict(realNamespace);
1790 CheckNamespaceValidityForNestedRelations(value);
1791 DoRaiseNamespaceChange();
1793 tableNamespace = value;
1795 finally{
1796 Bid.ScopeLeave(ref hscp);
1800 internal bool IsNamespaceInherited() {
1801 return (null == tableNamespace);
1804 internal void CheckCascadingNamespaceConflict(string realNamespace){
1805 foreach (DataRelation rel in ChildRelations)
1806 if ((rel.Nested) && (rel.ChildTable != this) && (rel.ChildTable.tableNamespace == null)) {
1807 DataTable childTable = rel.ChildTable;
1808 if (dataSet.Tables.Contains( childTable.TableName, realNamespace, false, true))
1809 throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
1811 childTable.CheckCascadingNamespaceConflict(realNamespace);
1816 internal void CheckNamespaceValidityForNestedRelations(string realNamespace){
1817 foreach(DataRelation rel in ChildRelations) {
1818 if (rel.Nested) {
1819 if (realNamespace != null) {
1820 rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(realNamespace, this);
1822 else{
1823 rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
1827 if (realNamespace == null) { // this will affect this table if it has parent relations
1828 this.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
1832 internal void CheckNamespaceValidityForNestedParentRelations(string ns, DataTable parentTable) {
1833 foreach(DataRelation rel in ParentRelations){
1834 if (rel.Nested) {
1835 if (rel.ParentTable != parentTable && rel.ParentTable.Namespace != ns) {
1836 throw ExceptionBuilder.InValidNestedRelation(this.TableName);
1843 internal void DoRaiseNamespaceChange(){
1844 RaisePropertyChanging("Namespace");
1845 // raise column Namespace change
1847 foreach (DataColumn col in Columns)
1848 if (col._columnUri == null)
1849 col.RaisePropertyChanging("Namespace");
1851 foreach (DataRelation rel in ChildRelations)
1852 if ((rel.Nested) && (rel.ChildTable != this)) {
1853 DataTable childTable = rel.ChildTable;
1855 rel.ChildTable.DoRaiseNamespaceChange();
1858 /// <devdoc>
1859 /// <para>
1860 /// Indicates whether the <see cref='System.Data.DataTable.Namespace'/> property should be persisted.
1861 /// </para>
1862 /// </devdoc>
1863 private bool ShouldSerializeNamespace() {
1864 return(tableNamespace != null);
1867 /// <devdoc>
1868 /// <para>
1869 /// Resets the <see cref='System.Data.DataTable.Namespace'/> property to its default state.
1870 /// </para>
1871 /// </devdoc>
1872 private void ResetNamespace() {
1873 this.Namespace = null;
1876 /// <devdoc>
1877 /// <para>[To be supplied.]</para>
1878 /// </devdoc>
1879 virtual public void BeginInit() {
1880 fInitInProgress = true;
1883 /// <devdoc>
1884 /// <para>[To be supplied.]</para>
1885 /// </devdoc>
1886 virtual public void EndInit() {
1887 if (dataSet == null || !dataSet.fInitInProgress) {
1888 Columns.FinishInitCollection();
1889 Constraints.FinishInitConstraints();
1890 foreach(DataColumn dc in Columns){
1891 if (dc.Computed) {
1892 dc.Expression = dc.Expression;
1896 fInitInProgress = false; // Microsoft : 77890. It is must that we set off this flag after calling FinishInitxxx();
1897 if (delayedSetPrimaryKey != null) {
1898 PrimaryKey = delayedSetPrimaryKey;
1899 delayedSetPrimaryKey = null;
1901 if (delayedViews.Count > 0) {
1902 foreach(DataView dv in delayedViews) {
1903 dv.EndInit();
1905 delayedViews.Clear();
1907 OnInitialized();
1910 /// <devdoc>
1911 /// <para>[To be supplied.]</para>
1912 /// </devdoc>
1914 DefaultValue(""),
1915 ResCategoryAttribute(Res.DataCategory_Data),
1916 ResDescriptionAttribute(Res.DataTablePrefixDescr)
1918 public string Prefix {
1919 get { return tablePrefix;}
1920 set {
1921 if (value == null) {
1922 value = "";
1924 Bid.Trace("<ds.DataTable.set_Prefix|API> %d#, value='%ls'\n", ObjectID, value);
1925 if ((XmlConvert.DecodeName(value) == value) &&
1926 (XmlConvert.EncodeName(value) != value))
1927 throw ExceptionBuilder.InvalidPrefix(value);
1930 tablePrefix = value;
1934 internal DataColumn XmlText {
1935 get {
1936 return xmlText;
1938 set {
1939 if (xmlText != value) {
1940 if (xmlText != null) {
1941 if (value != null) {
1942 throw ExceptionBuilder.MultipleTextOnlyColumns();
1944 Columns.Remove(xmlText);
1946 else {
1947 Debug.Assert(value != null, "Value shoud not be null ??");
1948 Debug.Assert(value.ColumnMapping == MappingType.SimpleContent, "should be text node here");
1949 if (value != Columns[value.ColumnName])
1950 Columns.Add(value);
1952 xmlText = value;
1957 internal decimal MaxOccurs {
1958 get {
1959 return maxOccurs;
1961 set {
1962 maxOccurs = value;
1966 internal decimal MinOccurs {
1967 get {
1968 return minOccurs;
1970 set {
1971 minOccurs = value;
1975 internal void SetKeyValues(DataKey key, object[] keyValues, int record) {
1976 for (int i = 0; i < keyValues.Length; i++) {
1977 key.ColumnsReference[i][record] = keyValues[i];
1981 internal DataRow FindByIndex(Index ndx, object[] key) {
1982 Range range = ndx.FindRecords(key);
1983 if (range.IsNull) {
1984 return null;
1986 return this.recordManager[ndx.GetRecord(range.Min)];
1989 internal DataRow FindMergeTarget(DataRow row, DataKey key, Index ndx) {
1990 DataRow targetRow = null;
1992 // Primary key match
1993 if (key.HasValue) {
1994 Debug.Assert(ndx != null);
1995 int findRecord = (row.oldRecord == -1) ? row.newRecord : row.oldRecord;
1996 object[] values = key.GetKeyValues(findRecord);
1997 targetRow = FindByIndex(ndx, values);
1999 return targetRow;
2002 private void SetMergeRecords(DataRow row, int newRecord, int oldRecord, DataRowAction action) {
2003 if (newRecord != -1) {
2004 SetNewRecord(row, newRecord, action, true, true);
2005 SetOldRecord(row, oldRecord);
2007 else {
2008 SetOldRecord(row, oldRecord);
2009 if (row.newRecord != -1) {
2010 Debug.Assert(action == DataRowAction.Delete, "Unexpected SetNewRecord action in merge function.");
2011 SetNewRecord(row, newRecord, action, true, true);
2016 internal DataRow MergeRow(DataRow row, DataRow targetRow, bool preserveChanges, Index idxSearch) {
2017 if (targetRow == null) {
2018 targetRow = this.NewEmptyRow();
2019 targetRow.oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
2020 targetRow.newRecord = targetRow.oldRecord;
2021 if(row.oldRecord != row.newRecord) {
2022 targetRow.newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
2024 InsertRow(targetRow, -1);
2026 else {
2027 // SQLBU 500789: Record Manager corruption during Merge when target row in edit state
2028 // the newRecord would be freed and overwrite tempRecord (which became the newRecord)
2029 // this would leave the DataRow referencing a freed record and leaking memory for the now lost record
2030 int proposedRecord = targetRow.tempRecord; // by saving off the tempRecord, EndEdit won't free newRecord
2031 targetRow.tempRecord = -1;
2032 try {
2033 DataRowState saveRowState = targetRow.RowState;
2034 int saveIdxRecord = (saveRowState == DataRowState.Added) ? targetRow.newRecord : saveIdxRecord = targetRow.oldRecord;
2035 int newRecord;
2036 int oldRecord;
2037 if (targetRow.RowState == DataRowState.Unchanged && row.RowState == DataRowState.Unchanged) {
2038 // unchanged row merging with unchanged row
2039 oldRecord = targetRow.oldRecord;
2040 newRecord = (preserveChanges) ? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
2041 oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, targetRow.oldRecord);
2042 SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
2044 else if (row.newRecord == -1) {
2045 // Incoming row is deleted
2046 oldRecord = targetRow.oldRecord;
2047 if (preserveChanges) {
2048 newRecord = (targetRow.RowState == DataRowState.Unchanged)? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
2050 else
2051 newRecord = -1;
2052 oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
2054 // Change index record, need to update index
2055 if (saveIdxRecord != ((saveRowState == DataRowState.Added) ? newRecord : oldRecord)) {
2056 SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
2057 idxSearch.Reset();
2058 saveIdxRecord = ((saveRowState == DataRowState.Added) ? newRecord : oldRecord);
2059 } else {
2060 SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
2063 else {
2064 // incoming row is added, modified or unchanged (targetRow is not unchanged)
2065 oldRecord = targetRow.oldRecord;
2066 newRecord = targetRow.newRecord;
2067 if (targetRow.RowState == DataRowState.Unchanged) {
2068 newRecord = recordManager.CopyRecord(this, oldRecord, -1);
2070 oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
2072 if (!preserveChanges) {
2073 newRecord = recordManager.CopyRecord(row.Table, row.newRecord, newRecord);
2075 SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
2078 if (saveRowState == DataRowState.Added && targetRow.oldRecord != -1)
2079 idxSearch.Reset();
2080 Debug.Assert(saveIdxRecord == ((saveRowState == DataRowState.Added) ? targetRow.newRecord : targetRow.oldRecord), "oops, you change index record without noticing it");
2082 finally {
2083 targetRow.tempRecord = proposedRecord;
2087 // Merge all errors
2088 if (row.HasErrors) {
2089 if (targetRow.RowError.Length == 0) {
2090 targetRow.RowError = row.RowError;
2091 } else {
2092 targetRow.RowError += " ]:[ " + row.RowError;
2094 DataColumn[] cols = row.GetColumnsInError();
2096 for (int i = 0; i < cols.Length; i++) {
2097 DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2098 targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2100 }else {
2101 if (!preserveChanges) {
2102 targetRow.ClearErrors();
2106 return targetRow;
2109 /// <devdoc>
2110 /// <para>Commits all the changes made to this table since the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
2111 /// </devdoc>
2112 public void AcceptChanges() {
2113 IntPtr hscp;
2114 Bid.ScopeEnter(out hscp, "<ds.DataTable.AcceptChanges|API> %d#\n", ObjectID);
2115 try {
2116 DataRow[] oldRows = new DataRow[Rows.Count];
2117 Rows.CopyTo(oldRows, 0);
2119 // delay updating of indexes until after all
2120 // AcceptChange calls have been completed
2121 SuspendIndexEvents();
2122 try {
2123 for (int i = 0; i < oldRows.Length; ++i) {
2124 if (oldRows[i].rowID != -1) {
2125 oldRows[i].AcceptChanges();
2129 finally {
2130 RestoreIndexEvents(false);
2133 finally{
2134 Bid.ScopeLeave(ref hscp);
2138 // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
2139 [MethodImpl(MethodImplOptions.NoInlining)]
2140 protected virtual DataTable CreateInstance() {
2141 return (DataTable) Activator.CreateInstance(this.GetType(), true);
2144 public virtual DataTable Clone() {
2145 return Clone(null);
2148 internal DataTable Clone(DataSet cloneDS) {
2149 IntPtr hscp;
2150 Bid.ScopeEnter(out hscp, "<ds.DataTable.Clone|INFO> %d#, cloneDS=%d\n", ObjectID, (cloneDS != null) ? cloneDS.ObjectID : 0);
2151 try {
2152 DataTable clone = CreateInstance();
2153 if (clone.Columns.Count > 0) // Microsoft : To clean up all the schema in strong typed dataset.
2154 clone.Reset();
2155 return CloneTo(clone, cloneDS, false);
2157 finally {
2158 Bid.ScopeLeave(ref hscp);
2163 private DataTable IncrementalCloneTo (DataTable sourceTable, DataTable targetTable) {
2164 foreach(DataColumn dc in sourceTable.Columns) {
2165 if (targetTable.Columns[dc.ColumnName] == null) {
2166 targetTable.Columns.Add(dc.Clone());
2170 return targetTable;
2173 private DataTable CloneHierarchy (DataTable sourceTable, DataSet ds, Hashtable visitedMap) {
2174 if (visitedMap == null)
2175 visitedMap = new Hashtable();
2176 if (visitedMap.Contains(sourceTable))
2177 return ((DataTable)visitedMap[sourceTable]);
2180 DataTable destinationTable = ds.Tables[sourceTable.TableName, sourceTable.Namespace];
2182 if ((destinationTable != null && destinationTable.Columns.Count > 0)) {
2183 destinationTable = IncrementalCloneTo(sourceTable,destinationTable);
2184 // get extra columns from source into destination , increamental read
2186 else {
2187 if (destinationTable == null) {
2188 destinationTable = new DataTable();
2189 // fxcop: new DataTable values for CaseSensitive, Locale, Namespace will come from CloneTo
2190 ds.Tables.Add(destinationTable);
2192 destinationTable = sourceTable.CloneTo(destinationTable, ds, true);
2194 visitedMap[sourceTable] = destinationTable;
2197 // start cloning relation
2198 foreach( DataRelation r in sourceTable.ChildRelations ) {
2199 DataTable childTable = CloneHierarchy((DataTable)r.ChildTable, ds, visitedMap);
2202 return destinationTable;
2206 private DataTable CloneTo(DataTable clone, DataSet cloneDS, bool skipExpressionColumns) {
2207 // we do clone datatables while we do readxmlschema, so we do not want to clone columnexpressions if we call this from ReadXmlSchema
2208 // it will cause exception to be thrown in cae expression refers to a table that is not in hirerachy or not created yet
2209 Debug.Assert(clone != null, "The table passed in has to be newly created empty DataTable.");
2211 // set All properties
2212 clone.tableName = tableName;
2214 clone.tableNamespace = tableNamespace;
2215 clone.tablePrefix = tablePrefix;
2216 clone.fNestedInDataset = fNestedInDataset;
2218 clone._culture = _culture;
2219 clone._cultureUserSet = _cultureUserSet;
2220 clone._compareInfo = _compareInfo;
2221 clone._compareFlags = _compareFlags;
2222 clone._formatProvider = _formatProvider;
2223 clone._hashCodeProvider = _hashCodeProvider;
2224 clone._caseSensitive = _caseSensitive;
2225 clone._caseSensitiveUserSet = _caseSensitiveUserSet;
2227 clone.displayExpression = displayExpression;
2228 clone.typeName = typeName; //Microsoft
2229 clone.repeatableElement = repeatableElement; //Microsoft
2230 clone.MinimumCapacity = MinimumCapacity;
2231 clone.RemotingFormat = RemotingFormat;
2232 // clone.SerializeHierarchy = SerializeHierarchy;
2234 // add all columns
2235 DataColumnCollection clmns = this.Columns;
2236 for (int i = 0; i < clmns.Count; i++) {
2237 clone.Columns.Add(clmns[i].Clone());
2240 // add all expressions if Clone is invoked only on DataTable otherwise DataSet.Clone will assign expressions after creating all relationships.
2241 if (!skipExpressionColumns && cloneDS == null) {
2242 for (int i = 0; i < clmns.Count; i++) {
2243 clone.Columns[clmns[i].ColumnName].Expression = clmns[i].Expression;
2247 // Create PrimaryKey
2248 DataColumn[] pkey = PrimaryKey;
2249 if (pkey.Length > 0) {
2250 DataColumn[] key = new DataColumn[pkey.Length];
2251 for (int i = 0; i < pkey.Length; i++) {
2252 key[i] = clone.Columns[pkey[i].Ordinal];
2254 clone.PrimaryKey = key;
2257 // now clone all unique constraints
2258 // Rename first
2259 for (int j = 0; j < Constraints.Count; j++) {
2260 ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
2261 UniqueConstraint unique = Constraints[j] as UniqueConstraint;
2262 if (foreign != null) {
2263 if (foreign.Table == foreign.RelatedTable) {
2264 ForeignKeyConstraint clonedConstraint = foreign.Clone(clone);
2265 Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
2266 if (oldConstraint != null) {
2267 oldConstraint.ConstraintName = Constraints[j].ConstraintName;
2271 else if (unique != null) {
2272 UniqueConstraint clonedConstraint = unique.Clone(clone);
2273 Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
2274 if (oldConstraint != null) {
2275 oldConstraint.ConstraintName = Constraints[j].ConstraintName;
2276 foreach (Object key in clonedConstraint.ExtendedProperties.Keys) {
2277 oldConstraint.ExtendedProperties[key] = clonedConstraint.ExtendedProperties[key];
2283 // then add
2284 for (int j = 0; j < Constraints.Count; j++) {
2285 if (! clone.Constraints.Contains(Constraints[j].ConstraintName, true)) {
2286 ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
2287 UniqueConstraint unique = Constraints[j] as UniqueConstraint;
2288 if (foreign != null) {
2289 if (foreign.Table == foreign.RelatedTable) {
2290 ForeignKeyConstraint newforeign = foreign.Clone(clone);
2291 if (newforeign != null) { // we cant make sure that we recieve a cloned FKC,since it depends if table and relatedtable be the same
2292 clone.Constraints.Add(newforeign);
2296 else if (unique != null) {
2297 clone.Constraints.Add(unique.Clone(clone));
2302 // ...Extended Properties...
2304 if (this.extendedProperties != null) {
2305 foreach(Object key in this.extendedProperties.Keys) {
2306 clone.ExtendedProperties[key]=this.extendedProperties[key];
2310 return clone;
2314 public DataTable Copy(){
2315 IntPtr hscp;
2316 Bid.ScopeEnter(out hscp, "<ds.DataTable.Copy|API> %d#\n", ObjectID);
2317 try {
2318 DataTable destTable = this.Clone();
2320 foreach (DataRow row in Rows)
2321 CopyRow(destTable, row);
2323 return destTable;
2325 finally {
2326 Bid.ScopeLeave(ref hscp);
2330 /// <devdoc>
2331 /// <para>Occurs when a value has been submitted for this column.</para>
2332 /// </devdoc>
2333 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangingDescr)]
2334 public event DataColumnChangeEventHandler ColumnChanging {
2335 add {
2336 Bid.Trace("<ds.DataTable.add_ColumnChanging|API> %d#\n", ObjectID);
2337 onColumnChangingDelegate += value;
2339 remove {
2340 Bid.Trace("<ds.DataTable.remove_ColumnChanging|API> %d#\n", ObjectID);
2341 onColumnChangingDelegate -= value;
2345 /// <devdoc>
2346 /// <para>[To be supplied.]</para>
2347 /// </devdoc>
2348 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangedDescr)]
2349 public event DataColumnChangeEventHandler ColumnChanged {
2350 add {
2351 Bid.Trace("<ds.DataTable.add_ColumnChanged|API> %d#\n", ObjectID);
2352 onColumnChangedDelegate += value;
2354 remove {
2355 Bid.Trace("<ds.DataTable.remove_ColumnChanged|API> %d#\n", ObjectID);
2356 onColumnChangedDelegate -= value;
2361 ResCategoryAttribute(Res.DataCategory_Action),
2362 ResDescriptionAttribute(Res.DataSetInitializedDescr)
2364 public event System.EventHandler Initialized {
2365 add {
2366 onInitialized += value;
2368 remove {
2369 onInitialized -= value;
2373 internal event PropertyChangedEventHandler PropertyChanging {
2374 add {
2375 Bid.Trace("<ds.DataTable.add_PropertyChanging|INFO> %d#\n", ObjectID);
2376 onPropertyChangingDelegate += value;
2378 remove {
2379 Bid.Trace("<ds.DataTable.remove_PropertyChanging|INFO> %d#\n", ObjectID);
2380 onPropertyChangingDelegate -= value;
2384 /// <devdoc>
2385 /// <para>
2386 /// Occurs after a row in the table has been successfully edited.
2387 /// </para>
2388 /// </devdoc>
2389 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangedDescr)]
2390 public event DataRowChangeEventHandler RowChanged {
2391 add {
2392 Bid.Trace("<ds.DataTable.add_RowChanged|API> %d#\n", ObjectID);
2393 onRowChangedDelegate += value;
2395 remove {
2396 Bid.Trace("<ds.DataTable.remove_RowChanged|API> %d#\n", ObjectID);
2397 onRowChangedDelegate -= value;
2401 /// <devdoc>
2402 /// <para>
2403 /// Occurs when the <see cref='System.Data.DataRow'/> is changing.
2404 /// </para>
2405 /// </devdoc>
2406 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangingDescr)]
2407 public event DataRowChangeEventHandler RowChanging {
2408 add {
2409 Bid.Trace("<ds.DataTable.add_RowChanging|API> %d#\n", ObjectID);
2410 onRowChangingDelegate += value;
2412 remove {
2413 Bid.Trace("<ds.DataTable.remove_RowChanging|API> %d#\n", ObjectID);
2414 onRowChangingDelegate -= value;
2418 /// <devdoc>
2419 /// <para>
2420 /// Occurs before a row in the table is
2421 /// about to be deleted.
2422 /// </para>
2423 /// </devdoc>
2424 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletingDescr)]
2425 public event DataRowChangeEventHandler RowDeleting {
2426 add {
2427 Bid.Trace("<ds.DataTable.add_RowDeleting|API> %d#\n", ObjectID);
2428 onRowDeletingDelegate += value;
2430 remove {
2431 Bid.Trace("<ds.DataTable.remove_RowDeleting|API> %d#\n", ObjectID);
2432 onRowDeletingDelegate -= value;
2436 /// <devdoc>
2437 /// <para>
2438 /// Occurs after a row in the
2439 /// table has been deleted.
2440 /// </para>
2441 /// </devdoc>
2442 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletedDescr)]
2443 public event DataRowChangeEventHandler RowDeleted {
2444 add {
2445 Bid.Trace("<ds.DataTable.add_RowDeleted|API> %d#\n", ObjectID);
2446 onRowDeletedDelegate += value;
2448 remove {
2449 Bid.Trace("<ds.DataTable.remove_RowDeleted|API> %d#\n", ObjectID);
2450 onRowDeletedDelegate -= value;
2454 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearingDescr)]
2455 public event DataTableClearEventHandler TableClearing {
2456 add {
2457 Bid.Trace("<ds.DataTable.add_TableClearing|API> %d#\n", ObjectID);
2458 onTableClearingDelegate += value;
2460 remove {
2461 Bid.Trace("<ds.DataTable.remove_TableClearing|API> %d#\n", ObjectID);
2462 onTableClearingDelegate -= value;
2466 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearedDescr)]
2467 public event DataTableClearEventHandler TableCleared {
2468 add {
2469 Bid.Trace("<ds.DataTable.add_TableCleared|API> %d#\n", ObjectID);
2470 onTableClearedDelegate += value;
2472 remove {
2473 Bid.Trace("<ds.DataTable.remove_TableCleared|API> %d#\n", ObjectID);
2474 onTableClearedDelegate -= value;
2478 [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsNewRowDescr)]
2479 public event DataTableNewRowEventHandler TableNewRow {
2480 add {
2481 onTableNewRowDelegate += value;
2483 remove {
2484 onTableNewRowDelegate -= value;
2488 [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2489 public override ISite Site {
2490 get {
2491 return base.Site;
2493 set {
2494 ISite oldSite = Site;
2495 if (value == null && oldSite != null) {
2496 IContainer cont = oldSite.Container;
2498 if (cont != null) {
2499 for (int i = 0; i < Columns.Count; i++) {
2500 if (Columns[i].Site != null) {
2501 cont.Remove(Columns[i]);
2506 base.Site = value;
2510 internal DataRow AddRecords(int oldRecord, int newRecord) {
2511 DataRow row;
2512 if (oldRecord == -1 && newRecord == -1)
2514 row = NewRow(-1);
2515 AddRow(row);
2517 else
2519 row = NewEmptyRow();
2520 row.oldRecord = oldRecord;
2521 row.newRecord = newRecord;
2522 InsertRow(row, -1);
2524 return row;
2527 internal void AddRow(DataRow row) {
2528 AddRow(row, -1);
2531 internal void AddRow(DataRow row, int proposedID) {
2532 InsertRow(row, proposedID, -1);
2535 internal void InsertRow(DataRow row, int proposedID, int pos) {
2536 InsertRow(row, proposedID, pos, /*fireEvent*/true);
2539 internal void InsertRow(DataRow row, long proposedID, int pos, bool fireEvent) {
2540 Exception deferredException = null;
2542 if (row == null) {
2543 throw ExceptionBuilder.ArgumentNull("row");
2545 if (row.Table != this) {
2546 throw ExceptionBuilder.RowAlreadyInOtherCollection();
2548 if (row.rowID != -1) {
2549 throw ExceptionBuilder.RowAlreadyInTheCollection();
2551 row.BeginEdit(); // ensure something's there.
2553 int record = row.tempRecord;
2554 row.tempRecord = -1;
2556 if (proposedID == -1) {
2557 proposedID = this.nextRowID;
2560 bool rollbackOnException;
2561 if (rollbackOnException = (nextRowID <= proposedID)) { // WebData 109005
2562 nextRowID = checked(proposedID + 1);
2565 try {
2566 try {
2567 row.rowID = proposedID;
2568 // this method may cause DataView.OnListChanged in which another row may be added
2569 SetNewRecordWorker(row, record, DataRowAction.Add, false, false, pos, fireEvent, out deferredException); // now we do add the row to collection before OnRowChanged (RaiseRowChanged)
2571 catch {
2572 if (rollbackOnException && (nextRowID == proposedID+1)) {
2573 nextRowID = proposedID;
2575 row.rowID = -1;
2576 row.tempRecord = record;
2577 throw;
2580 // since expression evaluation occurred in SetNewRecordWorker, there may have been a problem that
2581 // was deferred to this point. If so, throw now since row has already been added.
2582 if (deferredException != null)
2583 throw deferredException;
2585 if (EnforceConstraints && !inLoad ) { // if we are evaluating expression, we need to validate constraints
2586 int columnCount = columnCollection.Count;
2587 for (int i = 0; i < columnCount; ++i) {
2588 DataColumn column = columnCollection[i];
2589 if (column.Computed) {
2590 column.CheckColumnConstraint(row, DataRowAction.Add);
2595 finally {
2596 row.ResetLastChangedColumn();// if expression is evaluated while adding, before return, we want to clear it
2600 internal void CheckNotModifying(DataRow row) {
2601 if (row.tempRecord != -1) {
2602 row.EndEdit();
2603 //throw ExceptionBuilder.ModifyingRow();
2607 /// <devdoc>
2608 /// <para>
2609 /// Clears the table of all data.</para>
2610 /// </devdoc>
2612 public void Clear() {
2613 Clear(true);
2615 internal void Clear(bool clearAll) {
2616 IntPtr hscp;
2617 Bid.ScopeEnter(out hscp, "<ds.DataTable.Clear|INFO> %d#, clearAll=%d{bool}\n", ObjectID, clearAll);
2619 try {
2620 Debug.Assert(null == rowDiffId, "wasn't previously cleared");
2621 rowDiffId = null;
2623 if (dataSet != null)
2624 dataSet.OnClearFunctionCalled(this);
2625 bool shouldFireClearEvents = (this.Rows.Count != 0); // if Rows is already empty, this is noop
2627 DataTableClearEventArgs e = null;
2628 if (shouldFireClearEvents) {
2629 e = new DataTableClearEventArgs (this);
2630 OnTableClearing(e);
2633 if (dataSet != null && dataSet.EnforceConstraints) {
2635 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
2636 ForeignKeyConstraint constraint = constraints.GetForeignKeyConstraint();
2637 constraint.CheckCanClearParentTable(this);
2641 recordManager.Clear(clearAll);
2643 // SQLBU 415729: Serious performance issue when calling Clear()
2644 // this improves performance by iterating over rows instead of computing by index
2645 foreach(DataRow row in Rows) {
2646 row.oldRecord = -1;
2647 row.newRecord = -1;
2648 row.tempRecord = -1;
2649 row.rowID = -1;
2650 row.RBTreeNodeId = 0;
2652 Rows.ArrayClear();
2654 ResetIndexes();
2656 if (shouldFireClearEvents) {
2657 OnTableCleared(e);
2660 // SQLBU 501916 - DataTable internal index is corrupted:'5'
2661 foreach(DataColumn column in Columns) {
2662 EvaluateDependentExpressions(column);
2665 finally {
2666 Bid.ScopeLeave(ref hscp);
2670 internal void CascadeAll(DataRow row, DataRowAction action) {
2671 if (DataSet != null && DataSet.fEnableCascading) {
2672 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
2673 constraints.GetForeignKeyConstraint().CheckCascade(row, action);
2678 internal void CommitRow(DataRow row) {
2679 // Fire Changing event
2680 DataRowChangeEventArgs drcevent = OnRowChanging(null, row, DataRowAction.Commit);
2682 if (!inDataLoad)
2683 CascadeAll(row, DataRowAction.Commit);
2685 SetOldRecord(row, row.newRecord);
2687 OnRowChanged(drcevent, row, DataRowAction.Commit);
2690 internal int Compare(string s1, string s2) {
2691 return Compare(s1, s2, null);
2694 internal int Compare(string s1, string s2, CompareInfo comparer) {
2695 object obj1 = s1;
2696 object obj2 = s2;
2697 if (obj1 == obj2)
2698 return 0;
2699 if (obj1 == null)
2700 return -1;
2701 if (obj2 == null)
2702 return 1;
2704 int leng1 = s1.Length;
2705 int leng2 = s2.Length;
2707 for (; leng1 > 0; leng1--) {
2708 if (s1[leng1-1] != 0x20 && s1[leng1-1] != 0x3000) // 0x3000 is Ideographic Whitespace
2709 break;
2711 for (; leng2 > 0; leng2--) {
2712 if (s2[leng2-1] != 0x20 && s2[leng2-1] != 0x3000)
2713 break;
2716 return (comparer ?? this.CompareInfo).Compare(s1, 0, leng1, s2, 0, leng2, _compareFlags);
2719 internal int IndexOf(string s1, string s2) {
2720 return CompareInfo.IndexOf(s1, s2, _compareFlags);
2723 internal bool IsSuffix(string s1, string s2) {
2724 return CompareInfo.IsSuffix(s1, s2, _compareFlags);
2727 /// <devdoc>
2728 /// <para>Computes the given expression on the current rows that pass the filter criteria.</para>
2729 /// </devdoc>
2730 public object Compute(string expression, string filter) {
2731 DataRow[] rows = Select(filter, "", DataViewRowState.CurrentRows);
2732 DataExpression expr = new DataExpression(this, expression);
2733 return expr.Evaluate(rows);
2736 bool System.ComponentModel.IListSource.ContainsListCollection {
2737 get {
2738 return false;
2742 internal void CopyRow(DataTable table, DataRow row)
2744 int oldRecord = -1, newRecord = -1;
2746 if (row == null)
2747 return;
2749 if (row.oldRecord != -1) {
2750 oldRecord = table.recordManager.ImportRecord(row.Table, row.oldRecord);
2752 if (row.newRecord != -1) {
2753 if (row.newRecord != row.oldRecord) {
2754 newRecord = table.recordManager.ImportRecord(row.Table, row.newRecord);
2756 else
2757 newRecord = oldRecord;
2760 DataRow targetRow = table.AddRecords(oldRecord, newRecord);
2762 if (row.HasErrors) {
2763 targetRow.RowError = row.RowError;
2765 DataColumn[] cols = row.GetColumnsInError();
2767 for (int i = 0; i < cols.Length; i++) {
2768 DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2769 targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2776 internal void DeleteRow(DataRow row) {
2777 if (row.newRecord == -1) {
2778 throw ExceptionBuilder.RowAlreadyDeleted();
2781 // Store.PrepareForDelete(row);
2782 SetNewRecord(row, -1, DataRowAction.Delete, false, true);
2785 private void CheckPrimaryKey() {
2786 if (primaryKey == null) throw ExceptionBuilder.TableMissingPrimaryKey();
2789 internal DataRow FindByPrimaryKey(object[] values) {
2790 CheckPrimaryKey();
2791 return FindRow(primaryKey.Key, values);
2794 internal DataRow FindByPrimaryKey(object value) {
2795 CheckPrimaryKey();
2796 return FindRow(primaryKey.Key, value);
2799 private DataRow FindRow(DataKey key, object[] values) {
2800 Index index = GetIndex(NewIndexDesc(key));
2801 Range range = index.FindRecords(values);
2802 if (range.IsNull)
2803 return null;
2804 return recordManager[index.GetRecord(range.Min)];
2807 private DataRow FindRow(DataKey key, object value) {
2808 Index index = GetIndex(NewIndexDesc(key));
2809 Range range = index.FindRecords(value);
2810 if (range.IsNull)
2811 return null;
2812 return recordManager[index.GetRecord(range.Min)];
2815 internal string FormatSortString(IndexField[] indexDesc) {
2816 StringBuilder builder = new StringBuilder();
2817 foreach (IndexField field in indexDesc) {
2818 if (0 < builder.Length) {
2819 builder.Append(", ");
2821 builder.Append(field.Column.ColumnName);
2822 if (field.IsDescending) {
2823 builder.Append(" DESC");
2826 return builder.ToString();
2829 internal void FreeRecord(ref int record) {
2830 recordManager.FreeRecord(ref record);
2833 public DataTable GetChanges() {
2834 IntPtr hscp;
2835 Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#\n", ObjectID);
2836 try {
2837 DataTable dtChanges = this.Clone();
2838 DataRow row = null;
2840 for (int i = 0; i < Rows.Count; i++) {
2841 row = Rows[i];
2842 if (row.oldRecord != row.newRecord)
2843 dtChanges.ImportRow(row);
2846 if (dtChanges.Rows.Count == 0)
2847 return null;
2849 return dtChanges;
2851 finally {
2852 Bid.ScopeLeave(ref hscp);
2856 public DataTable GetChanges(DataRowState rowStates)
2858 IntPtr hscp;
2859 Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#, rowStates=%d{ds.DataRowState}\n", ObjectID, (int)rowStates);
2860 try {
2861 DataTable dtChanges = this.Clone();
2862 DataRow row = null;
2864 // check that rowStates is valid DataRowState
2865 Debug.Assert(Enum.GetUnderlyingType(typeof(DataRowState)) == typeof(Int32), "Invalid DataRowState type");
2867 for (int i = 0; i < Rows.Count; i++) {
2868 row = Rows[i];
2869 if ((row.RowState & rowStates) != 0)
2870 dtChanges.ImportRow(row);
2873 if (dtChanges.Rows.Count == 0)
2874 return null;
2876 return dtChanges;
2878 finally {
2879 Bid.ScopeLeave(ref hscp);
2883 /// <devdoc>
2884 /// <para>Returns an array of <see cref='System.Data.DataRow'/> objects that contain errors.</para>
2885 /// </devdoc>
2886 public DataRow[] GetErrors() {
2887 List<DataRow> errorList = new List<DataRow>();
2889 for (int i = 0; i < Rows.Count; i++) {
2890 DataRow row = Rows[i];
2891 if (row.HasErrors) {
2892 errorList.Add(row);
2895 DataRow[] temp = NewRowArray(errorList.Count);
2896 errorList.CopyTo(temp);
2897 return temp;
2900 internal Index GetIndex(IndexField[] indexDesc) {
2901 return GetIndex(indexDesc, DataViewRowState.CurrentRows, (IFilter)null);
2904 internal Index GetIndex(string sort, DataViewRowState recordStates, IFilter rowFilter) {
2905 return GetIndex(ParseSortString(sort), recordStates, rowFilter);
2908 internal Index GetIndex(IndexField[] indexDesc, DataViewRowState recordStates, IFilter rowFilter) {
2909 indexesLock.AcquireReaderLock(-1);
2910 try {
2911 for (int i = 0; i < indexes.Count; i++) {
2912 Index index = indexes[i];
2913 if (index != null) {
2914 if (index.Equal(indexDesc, recordStates, rowFilter)) {
2915 return index;
2920 finally {
2921 indexesLock.ReleaseReaderLock();
2923 Index ndx = new Index(this, indexDesc, recordStates, rowFilter);
2924 ndx.AddRef();
2925 return ndx;
2928 IList System.ComponentModel.IListSource.GetList() {
2929 return DefaultView;
2933 internal List<DataViewListener> GetListeners() {
2934 return _dataViewListeners;
2937 // We need a HashCodeProvider for Case, Kana and Width insensitive
2938 internal int GetSpecialHashCode(string name) {
2939 int i;
2940 for (i = 0; (i < name.Length) && (0x3000 > name[i]); ++i);
2942 if (name.Length == i) {
2943 if (null == _hashCodeProvider) {
2944 // it should use the CaseSensitive property, but V1 shipped this way
2945 _hashCodeProvider = StringComparer.Create(Locale, true);
2947 return _hashCodeProvider.GetHashCode(name);
2949 else {
2950 return 0;
2954 public void ImportRow(DataRow row)
2956 IntPtr hscp;
2957 Bid.ScopeEnter(out hscp, "<ds.DataTable.ImportRow|API> %d#\n", ObjectID);
2958 try {
2959 int oldRecord = -1, newRecord = -1;
2961 if (row == null)
2962 return;
2964 if (row.oldRecord != -1) {
2965 oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
2967 if (row.newRecord != -1) { // row not deleted
2968 if (row.RowState != DataRowState.Unchanged) { // not unchanged, it means Added or modified
2969 newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
2971 else
2972 newRecord = oldRecord;
2975 if (oldRecord != -1 || newRecord != -1) {
2976 DataRow targetRow = AddRecords(oldRecord, newRecord);
2978 if (row.HasErrors) {
2979 targetRow.RowError = row.RowError;
2981 DataColumn[] cols = row.GetColumnsInError();
2983 for (int i = 0; i < cols.Length; i++) {
2984 DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2985 targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2990 finally {
2991 Bid.ScopeLeave(ref hscp);
2996 internal void InsertRow(DataRow row, long proposedID) {
2997 IntPtr hscp;
2999 Bid.ScopeEnter(out hscp, "<ds.DataTable.InsertRow|INFO> %d#, row=%d\n", ObjectID, row.ObjectID);
3000 try {
3001 if (row.Table != this) {
3002 throw ExceptionBuilder.RowAlreadyInOtherCollection();
3004 if (row.rowID != -1) {
3005 throw ExceptionBuilder.RowAlreadyInTheCollection();
3007 if (row.oldRecord == -1 && row.newRecord == -1) {
3008 throw ExceptionBuilder.RowEmpty();
3011 if (proposedID == -1)
3012 proposedID = nextRowID;
3014 row.rowID = proposedID;
3015 if (nextRowID <= proposedID)
3016 nextRowID = checked(proposedID + 1);
3018 DataRowChangeEventArgs drcevent = null;
3021 if (row.newRecord != -1) {
3022 row.tempRecord = row.newRecord;
3023 row.newRecord = -1;
3025 try {
3026 drcevent = RaiseRowChanging(null, row, DataRowAction.Add, true);
3028 catch {
3029 row.tempRecord = -1;
3030 throw;
3033 row.newRecord = row.tempRecord;
3034 row.tempRecord = -1;
3037 if (row.oldRecord != -1)
3038 recordManager[row.oldRecord] = row;
3040 if (row.newRecord != -1)
3041 recordManager[row.newRecord] = row;
3043 Rows.ArrayAdd(row); // SQL BU Defect Tracking 247738, 323482 row should be in the
3044 // collection when maintaining the indexes
3046 if (row.RowState == DataRowState.Unchanged){ // how about row.oldRecord == row.newRecord both == -1
3047 RecordStateChanged(row.oldRecord, DataViewRowState.None, DataViewRowState.Unchanged);
3049 else {
3050 RecordStateChanged(row.oldRecord, DataViewRowState.None, row.GetRecordState(row.oldRecord),
3051 row.newRecord, DataViewRowState.None, row.GetRecordState(row.newRecord));
3054 if (dependentColumns != null && dependentColumns.Count > 0)
3055 EvaluateExpressions(row, DataRowAction.Add, null);
3057 RaiseRowChanged(drcevent, row, DataRowAction.Add);
3059 finally {
3060 Bid.ScopeLeave(ref hscp);
3064 private IndexField [] NewIndexDesc(DataKey key) {
3065 Debug.Assert(key.HasValue);
3066 IndexField[] indexDesc = key.GetIndexDesc();
3067 IndexField[] newIndexDesc = new IndexField[indexDesc.Length];
3068 Array.Copy(indexDesc, 0, newIndexDesc, 0, indexDesc.Length);
3069 return newIndexDesc;
3072 internal int NewRecord() {
3073 return NewRecord(-1);
3076 internal int NewUninitializedRecord() {
3077 return recordManager.NewRecordBase();
3080 internal int NewRecordFromArray(object[] value) {
3081 int colCount = columnCollection.Count; // Perf: use the readonly columnCollection field directly
3082 if (colCount < value.Length) {
3083 throw ExceptionBuilder.ValueArrayLength();
3085 int record = recordManager.NewRecordBase();
3086 try {
3087 for (int i = 0; i < value.Length; i++) {
3088 if (null != value[i]) {
3089 columnCollection[i][record] = value[i];
3091 else {
3092 columnCollection[i].Init(record); // Increase AutoIncrementCurrent
3095 for (int i = value.Length; i < colCount; i++) {
3096 columnCollection[i].Init(record);
3098 return record;
3100 catch (Exception e) {
3102 if (Common.ADP.IsCatchableOrSecurityExceptionType (e)) {
3103 FreeRecord(ref record); // WebData 104246
3105 throw;
3109 internal int NewRecord(int sourceRecord) {
3110 int record = recordManager.NewRecordBase();
3112 int count = columnCollection.Count;
3113 if (-1 == sourceRecord) {
3114 for (int i = 0; i < count; ++i) {
3115 columnCollection[i].Init(record);
3118 else {
3119 for (int i = 0; i < count; ++i) {
3120 columnCollection[i].Copy(sourceRecord, record);
3123 return record;
3126 internal DataRow NewEmptyRow() {
3127 rowBuilder._record = -1;
3128 DataRow dr = NewRowFromBuilder( rowBuilder );
3129 if (dataSet != null) {
3130 DataSet.OnDataRowCreated( dr );
3132 return dr;
3135 private DataRow NewUninitializedRow() {
3136 DataRow dr = NewRow(NewUninitializedRecord());
3137 return dr;
3140 /// <devdoc>
3141 /// <para>Creates a new <see cref='System.Data.DataRow'/>
3142 /// with the same schema as the table.</para>
3143 /// </devdoc>
3144 public DataRow NewRow() {
3145 DataRow dr = NewRow(-1);
3146 NewRowCreated(dr); // this is the only API we want this event to be fired
3147 return dr;
3150 // Only initialize DataRelation mapping columns (approximately hidden columns)
3151 internal DataRow CreateEmptyRow() {
3152 DataRow row = this.NewUninitializedRow();
3154 foreach( DataColumn c in this.Columns ) {
3155 if (!XmlToDatasetMap.IsMappedColumn(c)) {
3156 if (!c.AutoIncrement) {
3157 if (c.AllowDBNull) {
3158 row[c] = DBNull.Value;
3160 else if(c.DefaultValue!=null){
3161 row[c] = c.DefaultValue;
3164 else {
3165 c.Init(row.tempRecord);
3169 return row;
3172 private void NewRowCreated(DataRow row) {
3173 if (null != onTableNewRowDelegate) {
3174 DataTableNewRowEventArgs eventArg = new DataTableNewRowEventArgs(row);
3175 OnTableNewRow(eventArg);
3179 internal DataRow NewRow(int record) {
3180 if (-1 == record) {
3181 record = NewRecord(-1);
3184 rowBuilder._record = record;
3185 DataRow row = NewRowFromBuilder( rowBuilder );
3186 recordManager[record] = row;
3188 if (dataSet != null)
3189 DataSet.OnDataRowCreated( row );
3191 return row;
3194 // This is what a subclassed dataSet overrides to create a new row.
3195 protected virtual DataRow NewRowFromBuilder(DataRowBuilder builder) {
3196 return new DataRow(builder);
3199 /// <devdoc>
3200 /// <para>Gets the row type.</para>
3201 /// </devdoc>
3202 protected virtual Type GetRowType() {
3203 return typeof(DataRow);
3206 // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
3207 [MethodImpl(MethodImplOptions.NoInlining)]
3208 protected internal DataRow[] NewRowArray(int size) {
3209 if (IsTypedDataTable) {
3210 if (0 == size) {
3211 if (null == EmptyDataRowArray) {
3212 EmptyDataRowArray = (DataRow[]) Array.CreateInstance(GetRowType(), 0);
3214 return EmptyDataRowArray;
3216 return (DataRow[]) Array.CreateInstance(GetRowType(), size);
3218 else {
3219 return ((0 == size) ? DataTable.zeroRows : new DataRow[size]);
3223 internal bool NeedColumnChangeEvents {
3224 get {
3225 return (IsTypedDataTable || (null != onColumnChangingDelegate) || (null != onColumnChangedDelegate));
3229 protected internal virtual void OnColumnChanging(DataColumnChangeEventArgs e) {
3230 // intentionally allow exceptions to bubble up. We haven't committed anything yet.
3231 Debug.Assert(e != null, "e should not be null");
3232 if (onColumnChangingDelegate != null) {
3233 Bid.Trace("<ds.DataTable.OnColumnChanging|INFO> %d#\n", ObjectID);
3234 onColumnChangingDelegate(this, e);
3238 protected internal virtual void OnColumnChanged(DataColumnChangeEventArgs e) {
3239 Debug.Assert(e != null, "e should not be null");
3240 if (onColumnChangedDelegate != null) {
3241 Bid.Trace("<ds.DataTable.OnColumnChanged|INFO> %d#\n", ObjectID);
3242 onColumnChangedDelegate(this, e);
3246 protected virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent) {
3247 if (onPropertyChangingDelegate != null) {
3248 Bid.Trace("<ds.DataTable.OnPropertyChanging|INFO> %d#\n", ObjectID);
3249 onPropertyChangingDelegate(this, pcevent);
3253 internal void OnRemoveColumnInternal(DataColumn column) {
3254 OnRemoveColumn(column);
3257 /// <devdoc>
3258 /// <para>Notifies the <see cref='System.Data.DataTable'/> that a <see cref='System.Data.DataColumn'/> is
3259 /// being removed.</para>
3260 /// </devdoc>
3261 protected virtual void OnRemoveColumn(DataColumn column) {
3264 private DataRowChangeEventArgs OnRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3265 if ((null != onRowChangedDelegate) || IsTypedDataTable) {
3266 if (null == args) {
3267 args = new DataRowChangeEventArgs(eRow, eAction);
3269 OnRowChanged(args);
3271 return args;
3274 private DataRowChangeEventArgs OnRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3275 if ((null != onRowChangingDelegate) || IsTypedDataTable) {
3276 if (null == args) {
3277 args = new DataRowChangeEventArgs(eRow, eAction);
3279 OnRowChanging(args);
3281 return args;
3284 /// <devdoc>
3285 /// <para>
3286 /// Raises the <see cref='System.Data.DataTable.RowChanged'/> event.
3287 /// </para>
3288 /// </devdoc>
3289 protected virtual void OnRowChanged(DataRowChangeEventArgs e) {
3290 Debug.Assert((null != e) && ((null != onRowChangedDelegate) || IsTypedDataTable), "OnRowChanged arguments");
3291 if (onRowChangedDelegate != null) {
3292 Bid.Trace("<ds.DataTable.OnRowChanged|INFO> %d#\n", ObjectID);
3293 onRowChangedDelegate(this, e);
3297 /// <devdoc>
3298 /// <para>
3299 /// Raises the <see cref='System.Data.DataTable.RowChanging'/> event.
3300 /// </para>
3301 /// </devdoc>
3302 protected virtual void OnRowChanging(DataRowChangeEventArgs e) {
3303 Debug.Assert((null != e) && ((null != onRowChangingDelegate) || IsTypedDataTable), "OnRowChanging arguments");
3304 if (onRowChangingDelegate != null) {
3305 Bid.Trace("<ds.DataTable.OnRowChanging|INFO> %d#\n", ObjectID);
3306 onRowChangingDelegate(this, e);
3310 /// <devdoc>
3311 /// <para>
3312 /// Raises the <see cref='System.Data.DataTable.OnRowDeleting'/> event.
3313 /// </para>
3314 /// </devdoc>
3315 protected virtual void OnRowDeleting(DataRowChangeEventArgs e) {
3316 Debug.Assert((null != e) && ((null != onRowDeletingDelegate) || IsTypedDataTable), "OnRowDeleting arguments");
3317 if (onRowDeletingDelegate != null) {
3318 Bid.Trace("<ds.DataTable.OnRowDeleting|INFO> %d#\n", ObjectID);
3319 onRowDeletingDelegate(this, e);
3323 /// <devdoc>
3324 /// <para>
3325 /// Raises the <see cref='System.Data.DataTable.OnRowDeleted'/> event.
3326 /// </para>
3327 /// </devdoc>
3328 protected virtual void OnRowDeleted(DataRowChangeEventArgs e) {
3329 Debug.Assert((null != e) && ((null != onRowDeletedDelegate) || IsTypedDataTable), "OnRowDeleted arguments");
3330 if (onRowDeletedDelegate != null) {
3331 Bid.Trace("<ds.DataTable.OnRowDeleted|INFO> %d#\n", ObjectID);
3332 onRowDeletedDelegate(this, e);
3336 protected virtual void OnTableCleared(DataTableClearEventArgs e) {
3337 if (onTableClearedDelegate != null) {
3338 Bid.Trace("<ds.DataTable.OnTableCleared|INFO> %d#\n", ObjectID);
3339 onTableClearedDelegate(this, e);
3343 protected virtual void OnTableClearing(DataTableClearEventArgs e) {
3344 if (onTableClearingDelegate != null) {
3345 Bid.Trace("<ds.DataTable.OnTableClearing|INFO> %d#\n", ObjectID);
3346 onTableClearingDelegate(this, e);
3350 protected virtual void OnTableNewRow(DataTableNewRowEventArgs e) {
3351 if (onTableNewRowDelegate != null) {
3352 Bid.Trace("<ds.DataTable.OnTableNewRow|INFO> %d#\n", ObjectID);
3353 onTableNewRowDelegate(this, e);
3357 private void OnInitialized() {
3358 if (onInitialized != null) {
3359 Bid.Trace("<ds.DataTable.OnInitialized|INFO> %d#\n", ObjectID);
3360 onInitialized(this, EventArgs.Empty);
3365 internal IndexField[] ParseSortString(string sortString) {
3366 IndexField[] indexDesc = zeroIndexField;
3367 if ((null != sortString) && (0 < sortString.Length)) {
3368 string[] split = sortString.Split(new char[] { ','});
3369 indexDesc = new IndexField[split.Length];
3371 for (int i = 0; i < split.Length; i++) {
3372 string current = split[i].Trim();
3374 // handle ASC and DESC.
3375 int length = current.Length;
3376 bool descending = false;
3377 if (length >= 5 && String.Compare(current, length - 4, " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0) {
3378 current = current.Substring(0, length - 4).Trim();
3380 else if (length >= 6 && String.Compare(current, length - 5, " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0) {
3381 descending = true;
3382 current = current.Substring(0, length - 5).Trim();
3385 // handle brackets.
3386 if (current.StartsWith("[", StringComparison.Ordinal)) {
3387 if (current.EndsWith("]", StringComparison.Ordinal)) {
3388 current = current.Substring(1, current.Length - 2);
3390 else {
3391 throw ExceptionBuilder.InvalidSortString(split[i]);
3395 // find the column.
3396 DataColumn column = Columns[current];
3397 if(column == null) {
3398 throw ExceptionBuilder.ColumnOutOfRange(current);
3400 indexDesc[i] = new IndexField(column, descending);
3403 return indexDesc;
3406 internal void RaisePropertyChanging(string name) {
3407 OnPropertyChanging(new PropertyChangedEventArgs(name));
3410 // Notify all indexes that record changed.
3411 // Only called when Error was changed.
3412 internal void RecordChanged(int record) {
3413 Debug.Assert (record != -1, "Record number must be given");
3414 SetShadowIndexes(); // how about new assert?
3415 try {
3416 int numIndexes = shadowIndexes.Count;
3417 for (int i = 0; i < numIndexes; i++) {
3418 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3419 if (0 < ndx.RefCount) {
3420 ndx.RecordChanged(record);
3424 finally{
3425 RestoreShadowIndexes();
3429 // for each index in liveindexes invok RecordChanged
3430 // oldIndex and newIndex keeps position of record before delete and after insert in each index in order
3431 // LiveIndexes[n-m] will have its information in oldIndex[n-m] and newIndex[n-m]
3432 internal void RecordChanged(int[] oldIndex, int[] newIndex) {
3433 SetShadowIndexes();
3434 Debug.Assert (oldIndex.Length == newIndex.Length, "Size oldIndexes and newIndexes should be the same");
3435 Debug.Assert (oldIndex.Length == shadowIndexes.Count, "Size of OldIndexes should be the same as size of Live indexes");
3436 try{
3437 int numIndexes = shadowIndexes.Count;
3438 for (int i = 0; i < numIndexes; i++) {
3439 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3440 if (0 < ndx.RefCount) {
3441 ndx.RecordChanged(oldIndex[i], newIndex[i]);
3446 finally{
3447 RestoreShadowIndexes();
3451 internal void RecordStateChanged(int record, DataViewRowState oldState, DataViewRowState newState) {
3452 SetShadowIndexes();
3453 try{
3454 int numIndexes = shadowIndexes.Count;
3455 for (int i = 0; i < numIndexes; i++) {
3456 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3457 if (0 < ndx.RefCount) {
3458 ndx.RecordStateChanged(record, oldState, newState);
3462 finally{
3463 RestoreShadowIndexes();
3465 // System.Data.XML.Store.Store.OnROMChanged(record, oldState, newState);
3469 internal void RecordStateChanged(int record1, DataViewRowState oldState1, DataViewRowState newState1,
3470 int record2, DataViewRowState oldState2, DataViewRowState newState2) {
3471 SetShadowIndexes();
3472 try{
3473 int numIndexes = shadowIndexes.Count;
3474 for (int i = 0; i < numIndexes; i++) {
3475 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3476 if (0 < ndx.RefCount) {
3477 if (record1 != -1 && record2 != -1)
3478 ndx.RecordStateChanged(record1, oldState1, newState1,
3479 record2, oldState2, newState2);
3480 else if (record1 != -1)
3481 ndx.RecordStateChanged(record1, oldState1, newState1);
3482 else if (record2 != -1)
3483 ndx.RecordStateChanged(record2, oldState2, newState2);
3487 finally {
3488 RestoreShadowIndexes();
3490 // System.Data.XML.Store.Store.OnROMChanged(record1, oldState1, newState1, record2, oldState2, newState2);
3494 // RemoveRecordFromIndexes removes the given record (using row and version) from all indexes and it stores and returns the position of deleted
3495 // record from each index
3496 // IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
3497 internal int[] RemoveRecordFromIndexes(DataRow row, DataRowVersion version) {
3498 int indexCount = LiveIndexes.Count;
3499 int [] positionIndexes = new int[indexCount];
3501 int recordNo = row.GetRecordFromVersion(version);
3502 DataViewRowState states = row.GetRecordState(recordNo);
3504 while (--indexCount >= 0) {
3505 if (row.HasVersion(version) && ((states & indexes[indexCount].RecordStates) != DataViewRowState.None)) {
3506 int index = indexes[indexCount].GetIndex(recordNo);
3507 if (index > -1) {
3508 positionIndexes [indexCount] = index;
3509 indexes[indexCount].DeleteRecordFromIndex(index); // this will delete the record from index and MUSt not fire event
3511 else {
3512 positionIndexes [indexCount] = -1; // this means record was not in index
3515 else {
3516 positionIndexes [indexCount] = -1; // this means record was not in index
3519 return positionIndexes;
3522 // InsertRecordToIndexes inserts the given record (using row and version) to all indexes and it stores and returns the position of inserted
3523 // record to each index
3524 // IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
3525 internal int[] InsertRecordToIndexes(DataRow row, DataRowVersion version) {
3526 int indexCount = LiveIndexes.Count;
3527 int [] positionIndexes = new int[indexCount];
3529 int recordNo = row.GetRecordFromVersion(version);
3530 DataViewRowState states = row.GetRecordState(recordNo);
3532 while (--indexCount >= 0) {
3533 if (row.HasVersion(version)) {
3534 if ((states & indexes[indexCount].RecordStates) != DataViewRowState.None) {
3535 positionIndexes [indexCount] = indexes[indexCount].InsertRecordToIndex(recordNo);
3537 else {
3538 positionIndexes [indexCount] = -1;
3542 return positionIndexes;
3545 internal void SilentlySetValue(DataRow dr, DataColumn dc, DataRowVersion version, object newValue) {
3546 // get record for version
3547 int record = dr.GetRecordFromVersion(version);
3549 bool equalValues = false;
3550 if (DataStorage.IsTypeCustomType(dc.DataType) && newValue != dc[record]) {
3551 // if UDT storage, need to check if reference changed. See bug 385182
3552 equalValues = false;
3554 else {
3555 equalValues = dc.CompareValueTo(record, newValue, true);
3558 // if expression has changed
3559 if (!equalValues) {
3560 int[] oldIndex = dr.Table.RemoveRecordFromIndexes(dr, version);// conditional, if it exists it will try to remove with no event fired
3561 dc.SetValue(record, newValue);
3562 int[] newIndex = dr.Table.InsertRecordToIndexes(dr, version);// conditional, it will insert if it qualifies, no event will be fired
3563 if (dr.HasVersion(version)) {
3564 if (version != DataRowVersion.Original) {
3565 dr.Table.RecordChanged(oldIndex, newIndex);
3567 if (dc.dependentColumns != null) {
3568 //BugBug - passing in null for cachedRows. This means expression columns as keys does not work when key changes.
3569 dc.Table.EvaluateDependentExpressions(dc.dependentColumns, dr, version, null);
3573 dr.ResetLastChangedColumn();
3576 /// <devdoc>
3577 /// <para>Rolls back all changes that have been made to the table
3578 /// since it was loaded, or the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
3579 /// </devdoc>
3580 public void RejectChanges() {
3581 IntPtr hscp;
3582 Bid.ScopeEnter(out hscp, "<ds.DataTable.RejectChanges|API> %d#\n", ObjectID);
3583 try{
3584 DataRow[] oldRows = new DataRow[Rows.Count];
3585 Rows.CopyTo(oldRows, 0);
3587 for (int i = 0; i < oldRows.Length; i++) {
3588 RollbackRow(oldRows[i]);
3591 finally{
3592 Bid.ScopeLeave(ref hscp);
3597 internal void RemoveRow(DataRow row, bool check) {
3598 if (row.rowID == -1) {
3599 throw ExceptionBuilder.RowAlreadyRemoved();
3602 if (check && dataSet != null) {
3603 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
3604 constraints.GetForeignKeyConstraint().CheckCanRemoveParentRow(row);
3608 int oldRecord = row.oldRecord;
3609 int newRecord = row.newRecord;
3611 DataViewRowState oldRecordStatePre = row.GetRecordState(oldRecord);
3612 DataViewRowState newRecordStatePre = row.GetRecordState(newRecord);
3614 row.oldRecord = -1;
3615 row.newRecord = -1;
3617 if (oldRecord == newRecord) {
3618 oldRecord = -1;
3621 RecordStateChanged(oldRecord, oldRecordStatePre, DataViewRowState.None,
3622 newRecord, newRecordStatePre, DataViewRowState.None);
3624 FreeRecord(ref oldRecord);
3625 FreeRecord(ref newRecord);
3627 row.rowID = -1;
3628 Rows.ArrayRemove(row);
3631 // Resets the table back to its original state.
3632 public virtual void Reset() {
3633 IntPtr hscp;
3634 Bid.ScopeEnter(out hscp, "<ds.DataTable.Reset|API> %d#\n", ObjectID);
3635 try {
3636 Clear();
3637 ResetConstraints();
3639 DataRelationCollection dr = this.ParentRelations;
3640 int count = dr.Count;
3641 while (count > 0) {
3642 count--;
3643 dr.RemoveAt(count);
3646 dr = this.ChildRelations;
3647 count = dr.Count;
3648 while (count > 0) {
3649 count--;
3650 dr.RemoveAt(count);
3653 Columns.Clear();
3654 indexes.Clear();
3656 finally{
3657 Bid.ScopeLeave(ref hscp);
3661 internal void ResetIndexes() {
3662 ResetInternalIndexes(null);
3665 internal void ResetInternalIndexes(DataColumn column) {
3666 Debug.Assert(null != indexes, "unexpected null indexes");
3667 SetShadowIndexes();
3668 try{
3669 // the length of shadowIndexes will not change
3670 // but the array instance may change during
3671 // events during Index.Reset
3672 int numIndexes = shadowIndexes.Count;
3673 for (int i = 0; i < numIndexes; i++) {
3674 Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3675 if (0 < ndx.RefCount) {
3676 if (null == column) {
3677 ndx.Reset();
3679 else {
3680 // SQLBU 501916: DataTable internal index is corrupted:'5'
3681 bool found = false;
3682 foreach(IndexField field in ndx.IndexFields) {
3683 if (Object.ReferenceEquals(column, field.Column)) {
3684 found = true;
3685 break;
3689 if (found) {
3690 ndx.Reset();
3696 finally {
3697 RestoreShadowIndexes();
3701 internal void RollbackRow(DataRow row) {
3702 row.CancelEdit();
3703 SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
3706 private DataRowChangeEventArgs RaiseRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3707 try {
3708 if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangedDelegate))) {
3709 args = OnRowChanged(args, eRow, eAction);
3711 // check if we deleting good row
3712 else if (DataRowAction.Delete == eAction && eRow.newRecord == -1 && (IsTypedDataTable || (null != onRowDeletedDelegate))) {
3713 if (null == args) {
3714 args = new DataRowChangeEventArgs(eRow, eAction);
3716 OnRowDeleted(args);
3719 catch (Exception f) {
3721 if (!Common.ADP.IsCatchableExceptionType(f)) {
3722 throw;
3724 ExceptionBuilder.TraceExceptionWithoutRethrow(f);
3725 // ignore the exception
3727 return args;
3730 private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3731 if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangingDelegate))) {
3732 eRow.inChangingEvent = true;
3734 // don't catch
3735 try {
3736 args = OnRowChanging(args, eRow, eAction);
3738 finally {
3739 eRow.inChangingEvent = false;
3742 // check if we deleting good row
3743 else if (DataRowAction.Delete == eAction && eRow.newRecord != -1 && (IsTypedDataTable || (null != onRowDeletingDelegate))) {
3744 eRow.inDeletingEvent = true;
3745 // don't catch
3746 try {
3747 if (null == args) {
3748 args = new DataRowChangeEventArgs(eRow, eAction);
3750 OnRowDeleting(args);
3752 finally {
3753 eRow.inDeletingEvent = false;
3756 return args;
3759 private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, bool fireEvent) {
3761 // check all constraints
3762 if (EnforceConstraints && !inLoad ) {
3763 int columnCount = columnCollection.Count;
3764 for(int i = 0; i < columnCount; ++i) {
3765 DataColumn column = columnCollection[i];
3766 if (!column.Computed || eAction != DataRowAction.Add) {
3767 column.CheckColumnConstraint(eRow, eAction);
3771 int constraintCount = constraintCollection.Count;
3772 for(int i = 0; i < constraintCount; ++i) {
3773 constraintCollection[i].CheckConstraint(eRow, eAction);
3777 // $$anandra. Check this event out. May be an issue.
3778 if (fireEvent) {
3779 args = RaiseRowChanging(args, eRow, eAction);
3782 if (!inDataLoad) {
3783 // cascade things...
3784 if (!MergingData && eAction != DataRowAction.Nothing && eAction != DataRowAction.ChangeOriginal) {
3785 CascadeAll(eRow, eAction);
3788 return args;
3791 /// <devdoc>
3792 /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects.</para>
3793 /// </devdoc>
3794 public DataRow[] Select() {
3795 Bid.Trace("<ds.DataTable.Select|API> %d#\n", ObjectID);
3796 return new Select(this, "", "", DataViewRowState.CurrentRows).SelectRows();
3799 /// <devdoc>
3800 /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria in order of
3801 /// primary key (or lacking one, order of addition.)</para>
3802 /// </devdoc>
3803 public DataRow[] Select(string filterExpression) {
3804 Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls'\n", ObjectID, filterExpression);
3805 return new Select(this, filterExpression, "", DataViewRowState.CurrentRows).SelectRows();
3808 /// <devdoc>
3809 /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria, in the the
3810 /// specified sort order.</para>
3811 /// </devdoc>
3812 public DataRow[] Select(string filterExpression, string sort) {
3813 Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls'\n", ObjectID, filterExpression, sort);
3814 return new Select(this, filterExpression, sort, DataViewRowState.CurrentRows).SelectRows();
3817 /// <devdoc>
3818 /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter in the order of the
3819 /// sort, that match the specified state.</para>
3820 /// </devdoc>
3821 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) {
3822 Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls', recordStates=%d{ds.DataViewRowState}\n", ObjectID, filterExpression, sort, (int)recordStates);
3823 return new Select(this, filterExpression, sort, recordStates).SelectRows();
3826 internal void SetNewRecord(DataRow row, int proposedRecord, DataRowAction action = DataRowAction.Change, bool isInMerge = false, bool fireEvent = true, bool suppressEnsurePropertyChanged = false) {
3827 Exception deferredException = null;
3828 SetNewRecordWorker(row, proposedRecord, action, isInMerge, suppressEnsurePropertyChanged, -1, fireEvent, out deferredException); // we are going to call below overload from insert
3829 if (deferredException != null) {
3830 throw deferredException;
3834 private void SetNewRecordWorker(DataRow row, int proposedRecord, DataRowAction action, bool isInMerge, bool suppressEnsurePropertyChanged,
3835 int position, bool fireEvent, out Exception deferredException) {
3837 // this is the event workhorse... it will throw the changing/changed events
3838 // and update the indexes. Used by change, add, delete, revert.
3840 // order of execution is as follows
3842 // 1) set temp record
3843 // 2) Check constraints for non-expression columns
3844 // 3) Raise RowChanging/RowDeleting with temp record
3845 // 4) set the new record in storage
3846 // 5) Update indexes with recordStateChanges - this will fire ListChanged & PropertyChanged events on associated views
3847 // 6) Evaluate all Expressions (exceptions are deferred)- this will fire ListChanged & PropertyChanged events on associated views
3848 // 7) Raise RowChanged/ RowDeleted
3849 // 8) Check constraints for expression columns
3851 Debug.Assert(row != null, "Row can't be null.");
3852 deferredException = null;
3854 if (row.tempRecord != proposedRecord) {
3855 // $HACK: for performance reasons, EndUpdate calls SetNewRecord with tempRecord == proposedRecord
3856 if (!inDataLoad) {
3857 row.CheckInTable();
3858 CheckNotModifying(row);
3860 if (proposedRecord == row.newRecord) {
3861 if (isInMerge) {
3862 Debug.Assert(fireEvent, "SetNewRecord is called with wrong parameter");
3863 RaiseRowChanged(null, row, action);
3865 return;
3868 Debug.Assert(!row.inChangingEvent, "How can this row be in an infinite loop?");
3870 row.tempRecord = proposedRecord;
3872 DataRowChangeEventArgs drcevent = null;
3874 try {
3875 row._action = action;
3876 drcevent = RaiseRowChanging(null, row, action, fireEvent);
3878 catch {
3879 row.tempRecord = -1;
3880 throw;
3882 finally {
3883 row._action = DataRowAction.Nothing;
3886 row.tempRecord = -1;
3888 int currentRecord = row.newRecord;
3890 // if we're deleting, then the oldRecord value will change, so need to track that if it's distinct from the newRecord.
3891 int secondRecord = (proposedRecord != -1 ?
3892 proposedRecord :
3893 (row.RowState != DataRowState.Unchanged ?
3894 row.oldRecord :
3895 -1));
3897 if (action == DataRowAction.Add) { //if we come here from insert we do insert the row to collection
3898 if (position == -1)
3899 Rows.ArrayAdd(row);
3900 else
3901 Rows.ArrayInsert(row, position);
3904 List<DataRow> cachedRows = null;
3905 if ((action == DataRowAction.Delete || action == DataRowAction.Change)
3906 && dependentColumns != null && dependentColumns.Count > 0) {
3907 // if there are expression columns, need to cache related rows for deletes and updates (key changes)
3908 // before indexes are modified.
3909 cachedRows = new List<DataRow>();
3910 for (int j = 0; j < ParentRelations.Count; j++) {
3911 DataRelation relation = ParentRelations[j];
3912 if (relation.ChildTable != row.Table) {
3913 continue;
3915 cachedRows.InsertRange(cachedRows.Count, row.GetParentRows(relation));
3918 for (int j = 0; j < ChildRelations.Count; j++) {
3919 DataRelation relation = ChildRelations[j];
3920 if (relation.ParentTable != row.Table) {
3921 continue;
3923 cachedRows.InsertRange(cachedRows.Count, row.GetChildRows(relation));
3927 // Dev10 Bug 688779: DataRowView.PropertyChanged are not raised on RejectChanges
3928 // if the newRecord is changing, the propertychanged event should be allowed to triggered for ListChangedType.Changed or .Moved
3929 // unless the specific condition is known that no data has changed, like DataRow.SetModified()
3930 if (!suppressEnsurePropertyChanged && !row.HasPropertyChanged && (row.newRecord != proposedRecord)
3931 && (-1 != proposedRecord) // explictly not fixing Dev10 Bug 692044: DataRowView.PropertyChanged are not raised on DataTable.Delete when mixing current and original records in RowStateFilter
3932 && (-1 != row.newRecord)) // explictly not fixing parts of Dev10 Bug 697909: when mixing current and original records in RowStateFilter
3934 // DataRow will believe multiple edits occured and
3935 // DataView.ListChanged event w/ ListChangedType.ItemChanged will raise DataRowView.PropertyChanged event and
3936 // PropertyChangedEventArgs.PropertyName will now be empty string so
3937 // WPF will refresh the entire row
3938 row.LastChangedColumn = null;
3939 row.LastChangedColumn = null;
3942 // Check whether we need to update indexes
3943 if (LiveIndexes.Count != 0) {
3945 // Dev10 bug #463087: DataTable internal index is currupted: '5'
3946 if ((-1 == currentRecord) && (-1 != proposedRecord) && (-1 != row.oldRecord) && (proposedRecord != row.oldRecord)) {
3947 // the transition from DataRowState.Deleted -> DataRowState.Modified
3948 // with same orginal record but new current record
3949 // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
3950 // for indexes/views listening for both DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent
3951 currentRecord = row.oldRecord;
3954 DataViewRowState currentRecordStatePre = row.GetRecordState(currentRecord);
3955 DataViewRowState secondRecordStatePre = row.GetRecordState(secondRecord);
3957 row.newRecord = proposedRecord;
3958 if (proposedRecord != -1)
3959 this.recordManager[proposedRecord] = row;
3961 DataViewRowState currentRecordStatePost = row.GetRecordState(currentRecord);
3962 DataViewRowState secondRecordStatePost = row.GetRecordState(secondRecord);
3964 // may raise DataView.ListChanged event
3965 RecordStateChanged(currentRecord, currentRecordStatePre, currentRecordStatePost,
3966 secondRecord, secondRecordStatePre, secondRecordStatePost);
3968 else {
3969 row.newRecord = proposedRecord;
3970 if (proposedRecord != -1)
3971 this.recordManager[proposedRecord] = row;
3974 // Dev10 Bug 461199 - reset the last changed column here, after all
3975 // DataViews have raised their DataRowView.PropertyChanged event
3976 row.ResetLastChangedColumn();
3978 // SQLBU 278737: Record manager corruption when reentrant write operations
3979 // free the 'currentRecord' only after all the indexes have been updated.
3980 // Corruption! { if (currentRecord != row.oldRecord) { FreeRecord(ref currentRecord); } }
3981 // RecordStateChanged raises ListChanged event at which time user may do work
3982 if (-1 != currentRecord) {
3983 if (currentRecord != row.oldRecord)
3985 if ((currentRecord != row.tempRecord) && // Delete, AcceptChanges, BeginEdit
3986 (currentRecord != row.newRecord) && // RejectChanges & SetAdded
3987 (row == recordManager[currentRecord])) // AcceptChanges, NewRow
3989 FreeRecord(ref currentRecord);
3994 if (row.RowState == DataRowState.Detached && row.rowID != -1) {
3995 RemoveRow(row, false);
3998 if (dependentColumns != null && dependentColumns.Count > 0) {
3999 try {
4000 EvaluateExpressions(row, action, cachedRows);
4002 catch (Exception exc) {
4003 // For DataRows being added, throwing of exception from expression evaluation is
4004 // deferred until after the row has been completely added.
4005 if (action != DataRowAction.Add) {
4006 throw exc;
4008 else {
4009 deferredException = exc;
4014 try {
4015 if (fireEvent) {
4016 RaiseRowChanged(drcevent, row, action);
4019 catch (Exception e) {
4021 if (!Common.ADP.IsCatchableExceptionType(e)) {
4022 throw;
4024 ExceptionBuilder.TraceExceptionWithoutRethrow(e);
4025 // ignore the exception
4029 // this is the event workhorse... it will throw the changing/changed events
4030 // and update the indexes.
4031 internal void SetOldRecord(DataRow row, int proposedRecord) {
4032 if (!inDataLoad) {
4033 row.CheckInTable();
4034 CheckNotModifying(row);
4037 if (proposedRecord == row.oldRecord) {
4038 return;
4041 int originalRecord = row.oldRecord; // cache old record after potential RowChanging event
4042 try {
4043 // Check whether we need to update indexes
4044 if (LiveIndexes.Count != 0) {
4046 // Dev10 bug #463087: DataTable internal index is currupted: '5'
4047 if ((-1 == originalRecord) && (-1 != proposedRecord) && (-1 != row.newRecord) && (proposedRecord != row.newRecord)) {
4048 // the transition from DataRowState.Added -> DataRowState.Modified
4049 // with same current record but new original record
4050 // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
4051 // for indexes/views listening for both DataViewRowState.Added | DataViewRowState.ModifiedOriginal
4052 originalRecord = row.newRecord;
4055 DataViewRowState originalRecordStatePre = row.GetRecordState(originalRecord);
4056 DataViewRowState proposedRecordStatePre = row.GetRecordState(proposedRecord);
4058 row.oldRecord = proposedRecord;
4059 if (proposedRecord != -1)
4060 this.recordManager[proposedRecord] = row;
4062 DataViewRowState originalRecordStatePost = row.GetRecordState(originalRecord);
4063 DataViewRowState proposedRecordStatePost = row.GetRecordState(proposedRecord);
4065 RecordStateChanged(originalRecord, originalRecordStatePre, originalRecordStatePost,
4066 proposedRecord, proposedRecordStatePre, proposedRecordStatePost);
4068 else {
4069 row.oldRecord = proposedRecord;
4070 if (proposedRecord != -1)
4071 this.recordManager[proposedRecord] = row;
4074 finally {
4075 if ((originalRecord != -1) && (originalRecord != row.tempRecord) &&
4076 (originalRecord != row.oldRecord) && (originalRecord != row.newRecord)) {
4078 FreeRecord(ref originalRecord);
4080 // else during an event 'row.AcceptChanges(); row.BeginEdit(); row.EndEdit();'
4082 if (row.RowState == DataRowState.Detached && row.rowID != -1) {
4083 RemoveRow(row, false);
4088 private void RestoreShadowIndexes() {
4089 Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
4090 shadowCount--;
4091 if (0 == shadowCount) {
4092 shadowIndexes = null;
4096 private void SetShadowIndexes() {
4097 if (null == shadowIndexes) {
4098 Debug.Assert(0 == shadowCount, "unexpected count");
4099 shadowIndexes = LiveIndexes;
4100 shadowCount = 1;
4102 else {
4103 Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
4104 shadowCount++;
4108 internal void ShadowIndexCopy(){
4109 if (shadowIndexes == indexes) {
4110 Debug.Assert(0 < indexes.Count, "unexpected");
4111 shadowIndexes = new List<Index>(indexes);
4115 /// <devdoc>
4116 /// <para>Returns the <see cref='System.Data.DataTable.TableName'/> and <see cref='System.Data.DataTable.DisplayExpression'/>, if there is one as a concatenated string.</para>
4117 /// </devdoc>
4118 public override string ToString() {
4119 if (this.displayExpression == null)
4120 return this.TableName;
4121 else
4122 return this.TableName + " + " + this.DisplayExpressionInternal;
4125 /// <devdoc>
4126 /// <para>[To be supplied.]</para>
4127 /// </devdoc>
4128 public void BeginLoadData() {
4129 IntPtr hscp;
4130 Bid.ScopeEnter(out hscp, "<ds.DataTable.BeginLoadData|API> %d#\n", ObjectID);
4131 try {
4132 if (inDataLoad)
4133 return;
4135 inDataLoad = true;
4136 Debug.Assert(null == loadIndex, "loadIndex should already be null");
4137 loadIndex = null;
4138 // LoadDataRow may have been called before BeginLoadData and already
4139 // initialized loadIndexwithOriginalAdded & loadIndexwithCurrentDeleted
4141 initialLoad = (Rows.Count == 0);
4142 if(initialLoad) {
4143 SuspendIndexEvents();
4144 } else {
4145 if (primaryKey != null) {
4146 loadIndex = primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
4148 if(loadIndex != null) {
4149 loadIndex.AddRef();
4153 if (DataSet != null) {
4154 savedEnforceConstraints = DataSet.EnforceConstraints;
4155 DataSet.EnforceConstraints = false;
4157 else {
4158 this.EnforceConstraints = false;
4161 finally{
4162 Bid.ScopeLeave(ref hscp);
4166 /// <devdoc>
4167 /// <para>[To be supplied.]</para>
4168 /// </devdoc>
4169 public void EndLoadData() {
4170 IntPtr hscp;
4171 Bid.ScopeEnter(out hscp, "<ds.DataTable.EndLoadData|API> %d#\n", ObjectID);
4172 try {
4173 if (!inDataLoad)
4174 return;
4176 if(loadIndex != null) {
4177 loadIndex.RemoveRef();
4179 if (loadIndexwithOriginalAdded != null) {
4180 loadIndexwithOriginalAdded.RemoveRef();
4182 if (loadIndexwithCurrentDeleted != null) {
4183 loadIndexwithCurrentDeleted.RemoveRef();
4186 loadIndex = null;
4187 loadIndexwithOriginalAdded = null;
4188 loadIndexwithCurrentDeleted = null;
4190 inDataLoad = false;
4192 RestoreIndexEvents(false);
4194 if (DataSet != null)
4195 DataSet.EnforceConstraints = savedEnforceConstraints;
4196 else
4197 this.EnforceConstraints = true;
4199 finally{
4200 Bid.ScopeLeave(ref hscp);
4204 /// <devdoc>
4205 /// <para>Finds and updates a specific row. If no matching
4206 /// row is found, a new row is created using the given values.</para>
4207 /// </devdoc>
4208 public DataRow LoadDataRow(object[] values, bool fAcceptChanges) {
4209 IntPtr hscp;
4210 Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, fAcceptChanges=%d{bool}\n", ObjectID, fAcceptChanges);
4211 try {
4212 DataRow row;
4213 if (inDataLoad) {
4214 int record = NewRecordFromArray(values);
4215 if (loadIndex != null) {
4216 // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
4217 Debug.Assert(2 <= loadIndex.RefCount, "bad loadIndex.RefCount");
4219 int result = loadIndex.FindRecord(record);
4220 if (result != -1) {
4221 int resultRecord = loadIndex.GetRecord(result);
4222 row = recordManager[resultRecord];
4223 Debug.Assert (row != null, "Row can't be null for index record");
4224 row.CancelEdit();
4225 if (row.RowState == DataRowState.Deleted)
4226 SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
4227 SetNewRecord(row, record, DataRowAction.Change, false, true);
4228 if (fAcceptChanges)
4229 row.AcceptChanges();
4230 return row;
4233 row = NewRow(record);
4234 AddRow(row);
4235 if (fAcceptChanges)
4236 row.AcceptChanges();
4237 return row;
4239 else {
4240 // In case, BeginDataLoad is not called yet
4241 row = UpdatingAdd(values);
4242 if (fAcceptChanges)
4243 row.AcceptChanges();
4244 return row;
4247 finally{
4248 Bid.ScopeLeave(ref hscp);
4252 /// <devdoc>
4253 /// <para>Finds and updates a specific row. If no matching
4254 /// row is found, a new row is created using the given values.</para>
4255 /// </devdoc>
4256 public DataRow LoadDataRow(object[] values, LoadOption loadOption) {
4257 IntPtr hscp;
4258 Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID, (int)loadOption);
4259 try {
4260 Index indextoUse = null;
4261 if (this.primaryKey != null) {
4262 if (loadOption == LoadOption.Upsert) { // CurrentVersion, and Deleted
4263 if (loadIndexwithCurrentDeleted == null) {
4264 loadIndexwithCurrentDeleted = this.primaryKey.Key.GetSortIndex(DataViewRowState.CurrentRows |DataViewRowState.Deleted);
4265 Debug.Assert(loadIndexwithCurrentDeleted != null, "loadIndexwithCurrentDeleted should not be null" );
4266 if (loadIndexwithCurrentDeleted != null) {
4267 loadIndexwithCurrentDeleted.AddRef();
4270 indextoUse = loadIndexwithCurrentDeleted;
4272 else {// CurrentVersion, and Deleted : OverwriteRow, PreserveCurrentValues
4273 if (loadIndexwithOriginalAdded == null) {
4274 loadIndexwithOriginalAdded = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows |DataViewRowState.Added);
4275 Debug.Assert(loadIndexwithOriginalAdded != null, "loadIndexwithOriginalAdded should not be null");
4276 if (loadIndexwithOriginalAdded != null) {
4277 loadIndexwithOriginalAdded.AddRef();
4280 indextoUse = loadIndexwithOriginalAdded;
4282 // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
4283 Debug.Assert(2 <= indextoUse.RefCount, "bad indextoUse.RefCount");
4285 if(inDataLoad && !AreIndexEventsSuspended) { // we do not want to fire any listchanged in new Load/Fill
4286 SuspendIndexEvents();// so suspend events here(not suspended == table already has some rows initially)
4289 DataRow dataRow = LoadRow(values, loadOption, indextoUse);// if indextoUse == null, it means we dont have PK,
4290 // so LoadRow will take care of just adding the row to end
4292 return dataRow;
4294 finally {
4295 Bid.ScopeLeave(ref hscp);
4299 internal DataRow UpdatingAdd(object[] values) {
4300 Index index = null;
4301 if (this.primaryKey != null) {
4302 index = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
4305 if (index != null) {
4306 int record = NewRecordFromArray(values);
4307 int result = index.FindRecord(record);
4308 if (result != -1) {
4309 int resultRecord = index.GetRecord(result);
4310 DataRow row = this.recordManager[resultRecord];
4311 Debug.Assert (row != null, "Row can't be null for index record");
4312 row.RejectChanges();
4313 this.SetNewRecord(row, record);
4314 return row;
4316 DataRow row2 = NewRow(record);
4317 Rows.Add(row2);
4318 return row2;
4321 return Rows.Add(values);
4324 internal bool UpdatingCurrent(DataRow row, DataRowAction action) {
4325 return(action == DataRowAction.Add || action == DataRowAction.Change ||
4326 action == DataRowAction.Rollback || action == DataRowAction.ChangeOriginal ||
4327 action == DataRowAction.ChangeCurrentAndOriginal);
4328 // (action == DataRowAction.Rollback && row.tempRecord != -1));
4331 internal DataColumn AddUniqueKey(int position) {
4332 if (_colUnique != null)
4333 return _colUnique;
4335 // check to see if we can use already existant PrimaryKey
4336 DataColumn[] pkey = PrimaryKey;
4337 if (pkey.Length == 1)
4338 // We have one-column primary key, so we can use it in our heirarchical relation
4339 return pkey[0];
4341 // add Unique, but not primaryKey to the table
4343 string keyName = XMLSchema.GenUniqueColumnName(TableName + "_Id", this);
4344 DataColumn key = new DataColumn(keyName, typeof(Int32), null, MappingType.Hidden);
4345 key.Prefix = tablePrefix;
4346 key.AutoIncrement = true;
4347 key.AllowDBNull = false;
4348 key.Unique = true;
4350 if (position == -1)
4351 Columns.Add(key);
4352 else { // we do have a problem and Imy idea is it is bug. Ask Enzo while Code review. Why we do not set ordinal when we call AddAt?
4353 for(int i = Columns.Count -1; i >= position; i--) {
4354 this.Columns[i].SetOrdinalInternal(i+1);
4356 Columns.AddAt(position, key);
4357 key.SetOrdinalInternal(position);
4360 if (pkey.Length == 0)
4361 PrimaryKey = new DataColumn[] {
4365 _colUnique = key;
4366 return _colUnique;
4369 internal DataColumn AddUniqueKey() {
4370 return AddUniqueKey(-1);
4373 internal DataColumn AddForeignKey(DataColumn parentKey) {
4374 Debug.Assert(parentKey != null, "AddForeignKey: Invalid paramter.. related primary key is null");
4376 string keyName = XMLSchema.GenUniqueColumnName(parentKey.ColumnName, this);
4377 DataColumn foreignKey = new DataColumn(keyName, parentKey.DataType, null, MappingType.Hidden);
4378 Columns.Add(foreignKey);
4380 return foreignKey;
4383 internal void UpdatePropertyDescriptorCollectionCache() {
4384 propertyDescriptorCollectionCache = null;
4387 /// <devdoc>
4388 /// Retrieves an array of properties that the given component instance
4389 /// provides. This may differ from the set of properties the class
4390 /// provides. If the component is sited, the site may add or remove
4391 /// additional properties. The returned array of properties will be
4392 /// filtered by the given set of attributes.
4393 /// </devdoc>
4394 internal PropertyDescriptorCollection GetPropertyDescriptorCollection(Attribute[] attributes) {
4395 if (propertyDescriptorCollectionCache == null) {
4396 int columnsCount = Columns.Count;
4397 int relationsCount = ChildRelations.Count;
4398 PropertyDescriptor[] props = new PropertyDescriptor[columnsCount + relationsCount]; {
4399 for (int i = 0; i < columnsCount; i++) {
4400 props[i] = new DataColumnPropertyDescriptor(Columns[i]);
4402 for (int i = 0; i < relationsCount; i++) {
4403 props[columnsCount + i] = new DataRelationPropertyDescriptor(ChildRelations[i]);
4406 propertyDescriptorCollectionCache = new PropertyDescriptorCollection(props);
4408 return propertyDescriptorCollectionCache;
4411 internal XmlQualifiedName TypeName {
4412 get {
4413 return ((typeName == null) ? XmlQualifiedName.Empty : (XmlQualifiedName)typeName);
4415 set {
4416 typeName = value;
4420 public void Merge(DataTable table)
4422 Merge(table, false, MissingSchemaAction.Add);
4425 public void Merge(DataTable table, bool preserveChanges)
4427 Merge(table, preserveChanges, MissingSchemaAction.Add);
4430 public void Merge(DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
4432 IntPtr hscp;
4433 Bid.ScopeEnter(out hscp, "<ds.DataTable.Merge|API> %d#, table=%d, preserveChanges=%d{bool}, missingSchemaAction=%d{ds.MissingSchemaAction}\n", ObjectID, (table != null) ? table.ObjectID : 0, preserveChanges, (int)missingSchemaAction);
4434 try{
4435 if (table == null)
4436 throw ExceptionBuilder.ArgumentNull("table");
4438 switch(missingSchemaAction) { // @perfnote: Enum.IsDefined
4439 case MissingSchemaAction.Add:
4440 case MissingSchemaAction.Ignore:
4441 case MissingSchemaAction.Error:
4442 case MissingSchemaAction.AddWithKey:
4443 Merger merger = new Merger(this, preserveChanges, missingSchemaAction);
4444 merger.MergeTable(table);
4445 break;
4446 default:
4447 throw Common.ADP.InvalidMissingSchemaAction(missingSchemaAction);
4450 finally{
4451 Bid.ScopeLeave(ref hscp);
4455 public void Load (IDataReader reader){
4456 Load(reader, LoadOption.PreserveChanges, null);
4459 public void Load (IDataReader reader, LoadOption loadOption) {
4460 Load(reader, loadOption, null);
4463 public virtual void Load (IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler){
4464 IntPtr hscp;
4465 Bid.ScopeEnter(out hscp, "<ds.DataTable.Load|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID, (int)loadOption);
4466 try {
4467 if (this.PrimaryKey.Length == 0) {
4468 DataTableReader dtReader = reader as DataTableReader;
4469 if (dtReader != null && dtReader.CurrentDataTable == this)
4470 return; // if not return, it will go to infinite loop
4472 Common.LoadAdapter adapter = new Common.LoadAdapter();
4473 adapter.FillLoadOption = loadOption;
4474 adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
4475 if (null != errorHandler) {
4476 adapter.FillError += errorHandler;
4478 adapter.FillFromReader(new DataTable[] { this }, reader, 0, 0);
4480 if (!reader.IsClosed && !reader.NextResult()) { //
4481 reader.Close();
4484 finally {
4485 Bid.ScopeLeave(ref hscp);
4489 private DataRow LoadRow(object[] values, LoadOption loadOption, Index searchIndex) {
4490 int recordNo;
4491 DataRow dataRow = null;
4493 if (searchIndex != null) {
4494 int[] primaryKeyIndex = new int[0];
4495 if (this.primaryKey != null) { // I do check above for PK, but in case if someone else gives me some index unrelated to PK
4496 primaryKeyIndex = new int[this.primaryKey.ColumnsReference.Length];
4497 for(int i = 0; i < this.primaryKey.ColumnsReference.Length; i++) {
4498 primaryKeyIndex[i] = this.primaryKey.ColumnsReference[i].Ordinal;
4502 object[] keys = new object[primaryKeyIndex.Length];
4503 for(int i = 0; i < primaryKeyIndex.Length; i++) {
4504 keys[i] = values[primaryKeyIndex[i]];
4507 Range result = searchIndex.FindRecords(keys);
4509 if (!result.IsNull) {
4510 int deletedRowUpsertCount = 0;
4511 for(int i = result.Min; i <= result.Max; i++) {
4512 int resultRecord = searchIndex.GetRecord(i);
4513 dataRow = this.recordManager[resultRecord];
4514 recordNo = NewRecordFromArray(values);
4516 //SQLBU DT 33648
4517 // values array is being reused by DataAdapter, do not modify the values array
4518 for(int count = 0; count < values.Length; count++) {
4519 if (null == values[count]) {
4520 columnCollection[count].Copy(resultRecord, recordNo);
4523 for(int count = values.Length; count < columnCollection.Count ; count++) {
4524 columnCollection[count].Copy(resultRecord, recordNo); // if there are missing values
4527 if (loadOption != LoadOption.Upsert || dataRow.RowState != DataRowState.Deleted) {
4528 SetDataRowWithLoadOption(dataRow , recordNo, loadOption, true);
4530 else {
4531 deletedRowUpsertCount++;
4534 if (0 == deletedRowUpsertCount) {
4535 return dataRow;
4540 recordNo = NewRecordFromArray(values);
4541 dataRow = NewRow(recordNo);
4542 // fire rowChanging event here
4543 DataRowAction action;
4544 DataRowChangeEventArgs drcevent = null;
4545 switch(loadOption) {
4546 case LoadOption.OverwriteChanges:
4547 case LoadOption.PreserveChanges:
4548 action = DataRowAction.ChangeCurrentAndOriginal;
4549 break;
4550 case LoadOption.Upsert:
4551 action = DataRowAction.Add;
4552 break;
4553 default:
4554 throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4557 drcevent = RaiseRowChanging(null, dataRow, action);
4559 this.InsertRow (dataRow, -1, -1, false);
4560 switch(loadOption) {
4561 case LoadOption.OverwriteChanges:
4562 case LoadOption.PreserveChanges:
4563 this.SetOldRecord(dataRow, recordNo);
4564 break;
4565 case LoadOption.Upsert:
4566 break;
4567 default:
4568 throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4570 RaiseRowChanged(drcevent, dataRow, action);
4572 return dataRow;
4575 private void SetDataRowWithLoadOption (DataRow dataRow, int recordNo, LoadOption loadOption, bool checkReadOnly) {
4576 bool hasError = false;
4577 if (checkReadOnly) {
4578 foreach(DataColumn dc in this.Columns) {
4579 if (dc.ReadOnly && !dc.Computed) {
4580 switch(loadOption) {
4581 case LoadOption.OverwriteChanges:
4582 if ((dataRow[dc, DataRowVersion.Current] != dc[recordNo]) ||(dataRow[dc, DataRowVersion.Original] != dc[recordNo]))
4583 hasError = true;
4584 break;
4585 case LoadOption.Upsert:
4586 if (dataRow[dc, DataRowVersion.Current] != dc[recordNo])
4587 hasError = true;
4588 break;
4589 case LoadOption.PreserveChanges:
4590 if (dataRow[dc, DataRowVersion.Original] != dc[recordNo])
4591 hasError = true;
4592 break;
4596 } // No Event should be fired in SenNewRecord and SetOldRecord
4597 // fire rowChanging event here
4599 DataRowChangeEventArgs drcevent = null;
4600 DataRowAction action = DataRowAction.Nothing;
4601 int cacheTempRecord = dataRow.tempRecord;
4602 dataRow.tempRecord = recordNo;
4604 switch(loadOption) {
4605 case LoadOption.OverwriteChanges:
4606 action = DataRowAction.ChangeCurrentAndOriginal;
4607 break;
4608 case LoadOption.Upsert:
4609 switch(dataRow.RowState) {
4610 case DataRowState.Unchanged:
4611 // let see if the incomming value has the same values as existing row, so compare records
4612 foreach(DataColumn dc in dataRow.Table.Columns) {
4613 if (0 != dc.Compare(dataRow.newRecord, recordNo)) {
4614 action = DataRowAction.Change;
4615 break;
4618 break;
4619 case DataRowState.Deleted:
4620 Debug.Assert(false, "LoadOption.Upsert with deleted row, should not be here");
4621 break;
4622 default :
4623 action = DataRowAction.Change;
4624 break;
4626 break;
4627 case LoadOption.PreserveChanges:
4628 switch(dataRow.RowState) {
4629 case DataRowState.Unchanged:
4630 action = DataRowAction.ChangeCurrentAndOriginal;
4631 break;
4632 default:
4633 action = DataRowAction.ChangeOriginal;
4634 break;
4636 break;
4637 default:
4638 throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4641 try {
4642 drcevent = RaiseRowChanging(null, dataRow, action);
4643 if (action == DataRowAction.Nothing) { // RaiseRowChanging does not fire for DataRowAction.Nothing
4644 dataRow.inChangingEvent = true;
4645 try {
4646 drcevent = OnRowChanging(drcevent, dataRow, action);
4648 finally {
4649 dataRow.inChangingEvent = false;
4653 finally {
4654 Debug.Assert(dataRow.tempRecord == recordNo, "tempRecord has been changed in event handler");
4655 if (DataRowState.Detached == dataRow.RowState) {
4656 // 'row.Table.Remove(row);'
4657 if (-1 != cacheTempRecord) {
4658 FreeRecord(ref cacheTempRecord);
4661 else {
4662 if (dataRow.tempRecord != recordNo) {
4663 // 'row.EndEdit(); row.BeginEdit(); '
4664 if (-1 != cacheTempRecord) {
4665 FreeRecord(ref cacheTempRecord);
4667 if (-1 != recordNo) {
4668 FreeRecord(ref recordNo);
4670 recordNo = dataRow.tempRecord;
4672 else {
4673 dataRow.tempRecord = cacheTempRecord;
4677 if (dataRow.tempRecord != -1) {
4678 dataRow.CancelEdit();
4681 switch(loadOption) {
4682 case LoadOption.OverwriteChanges:
4683 this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
4684 this.SetOldRecord(dataRow, recordNo);
4685 break;
4686 case LoadOption.Upsert:
4687 if (dataRow.RowState == DataRowState.Unchanged) {
4688 this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
4689 if (!dataRow.HasChanges()) {
4690 this.SetOldRecord(dataRow, recordNo);
4693 else {
4694 if (dataRow.RowState == DataRowState.Deleted)
4695 dataRow.RejectChanges();
4696 this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
4698 break;
4699 case LoadOption.PreserveChanges:
4700 if (dataRow.RowState == DataRowState.Unchanged) {
4701 // SQLBU 500706: DataTable internal index is corrupted: '8'
4702 // if ListChanged event deletes dataRow
4703 this.SetOldRecord(dataRow, recordNo); // do not fire event
4704 this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
4706 else { // if modified/ added / deleted we want this operation to fire event (just for LoadOption.PreserveCurrentValues)
4707 this.SetOldRecord(dataRow, recordNo);
4709 break;
4710 default:
4711 throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4714 if (hasError) {
4715 string error = Res.GetString(Res.Load_ReadOnlyDataModified);
4716 if (dataRow.RowError.Length == 0) { // WebData 112272, append the row error
4717 dataRow.RowError = error;
4719 else {
4720 dataRow.RowError += " ]:[ " + error ;
4723 foreach(DataColumn dc in this.Columns) {
4724 if (dc.ReadOnly && !dc.Computed)
4725 dataRow.SetColumnError(dc, error);
4729 drcevent = RaiseRowChanged(drcevent, dataRow, action);
4730 if (action == DataRowAction.Nothing) { // RaiseRowChanged does not fire for DataRowAction.Nothing
4731 dataRow.inChangingEvent = true;
4732 try {
4733 OnRowChanged(drcevent, dataRow, action);
4735 finally {
4736 dataRow.inChangingEvent = false;
4742 public DataTableReader CreateDataReader() {
4743 return new DataTableReader(this);
4747 public void WriteXml(Stream stream)
4749 WriteXml(stream, XmlWriteMode.IgnoreSchema, false);
4752 public void WriteXml(Stream stream, bool writeHierarchy)
4754 WriteXml(stream, XmlWriteMode.IgnoreSchema, writeHierarchy);
4757 public void WriteXml(TextWriter writer)
4759 WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
4762 public void WriteXml(TextWriter writer, bool writeHierarchy)
4764 WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
4767 public void WriteXml(XmlWriter writer)
4769 WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
4772 public void WriteXml(XmlWriter writer, bool writeHierarchy)
4774 WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
4777 [ResourceExposure(ResourceScope.Machine)]
4778 [ResourceConsumption(ResourceScope.Machine)]
4779 public void WriteXml(String fileName)
4781 WriteXml(fileName, XmlWriteMode.IgnoreSchema, false);
4784 [ResourceExposure(ResourceScope.Machine)]
4785 [ResourceConsumption(ResourceScope.Machine)]
4786 public void WriteXml(String fileName, bool writeHierarchy)
4788 WriteXml(fileName, XmlWriteMode.IgnoreSchema, writeHierarchy);
4791 public void WriteXml(Stream stream, XmlWriteMode mode)
4793 WriteXml(stream, mode, false);
4796 public void WriteXml(Stream stream, XmlWriteMode mode, bool writeHierarchy)
4798 if (stream != null) {
4799 XmlTextWriter w = new XmlTextWriter(stream, null) ;
4800 w.Formatting = Formatting.Indented;
4802 WriteXml( w, mode, writeHierarchy);
4806 public void WriteXml(TextWriter writer, XmlWriteMode mode)
4808 WriteXml(writer, mode, false);
4811 public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writeHierarchy)
4813 if (writer != null) {
4814 XmlTextWriter w = new XmlTextWriter(writer) ;
4815 w.Formatting = Formatting.Indented;
4817 WriteXml(w, mode, writeHierarchy);
4821 public void WriteXml(XmlWriter writer, XmlWriteMode mode)
4823 WriteXml(writer, mode, false);
4825 public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writeHierarchy)
4827 IntPtr hscp;
4828 Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, mode=%d{ds.XmlWriteMode}\n", ObjectID, (int)mode);
4829 try{
4830 if (this.tableName.Length == 0) {
4831 throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
4833 // Generate SchemaTree and write it out
4834 if (writer != null) {
4836 if (mode == XmlWriteMode.DiffGram) { // FIX THIS
4837 // Create and save the updates
4838 new NewDiffgramGen(this, writeHierarchy).Save(writer, this);
4840 else {
4841 // Create and save xml data
4842 if (mode == XmlWriteMode.WriteSchema) {
4843 DataSet ds = null;
4844 string tablenamespace = this.tableNamespace;
4845 if (null == this.DataSet) {
4846 ds = new DataSet();
4847 // if user set values on DataTable, it isn't necessary
4848 // to set them on the DataSet because they won't be inherited
4849 // but it is simpler to set them in both places
4851 // if user did not set values on DataTable, it is required
4852 // to set them on the DataSet so the table will inherit
4853 // the value already on the Datatable
4854 ds.SetLocaleValue(_culture, _cultureUserSet);
4855 ds.CaseSensitive = this.CaseSensitive;
4856 ds.Namespace = this.Namespace;
4857 ds.RemotingFormat = this.RemotingFormat;
4858 ds.Tables.Add(this);
4861 if (writer != null) {
4862 XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
4863 xmldataWriter.Save(writer, /*mode == XmlWriteMode.WriteSchema*/true);
4865 if (null != ds) {
4866 ds.Tables.Remove(this);
4867 this.tableNamespace = tablenamespace;
4870 else {
4871 XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
4872 xmldataWriter.Save(writer,/*mode == XmlWriteMode.WriteSchema*/ false);
4877 finally {
4878 Bid.ScopeLeave(ref hscp);
4882 [ResourceExposure(ResourceScope.Machine)]
4883 [ResourceConsumption(ResourceScope.Machine)]
4884 public void WriteXml(String fileName, XmlWriteMode mode)
4886 WriteXml(fileName, mode, false);
4889 [ResourceExposure(ResourceScope.Machine)]
4890 [ResourceConsumption(ResourceScope.Machine)]
4891 public void WriteXml(String fileName, XmlWriteMode mode, bool writeHierarchy)
4893 IntPtr hscp;
4894 Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, fileName='%ls', mode=%d{ds.XmlWriteMode}\n", ObjectID, fileName, (int)mode);
4895 try {
4896 using(XmlTextWriter xw = new XmlTextWriter( fileName, null )) {
4897 xw.Formatting = Formatting.Indented;
4898 xw.WriteStartDocument(true);
4900 WriteXml(xw, mode, writeHierarchy);
4902 xw.WriteEndDocument();
4905 finally {
4906 Bid.ScopeLeave(ref hscp);
4910 public void WriteXmlSchema(Stream stream) {
4911 WriteXmlSchema(stream, false);
4914 public void WriteXmlSchema(Stream stream, bool writeHierarchy)
4916 if (stream == null)
4917 return;
4919 XmlTextWriter w = new XmlTextWriter(stream, null) ;
4920 w.Formatting = Formatting.Indented;
4922 WriteXmlSchema( w, writeHierarchy );
4925 public void WriteXmlSchema( TextWriter writer ) {
4926 WriteXmlSchema( writer, false );
4929 public void WriteXmlSchema( TextWriter writer, bool writeHierarchy )
4932 if (writer == null)
4933 return;
4935 XmlTextWriter w = new XmlTextWriter(writer);
4936 w.Formatting = Formatting.Indented;
4938 WriteXmlSchema( w, writeHierarchy );
4941 private bool CheckForClosureOnExpressions(DataTable dt, bool writeHierarchy) {
4942 List<DataTable> tableList = new List<DataTable>();
4943 tableList.Add(dt);
4944 if (writeHierarchy) { // WebData 112161
4945 CreateTableList(dt, tableList);
4947 return CheckForClosureOnExpressionTables(tableList);
4950 private bool CheckForClosureOnExpressionTables(List<DataTable> tableList) {
4951 Debug.Assert(tableList != null, "tableList shouldnot be null");
4953 foreach(DataTable datatable in tableList) {
4954 foreach(DataColumn dc in datatable.Columns) {
4955 if (dc.Expression.Length != 0) {
4956 DataColumn[] dependency = dc.DataExpression.GetDependency();
4957 for (int j = 0; j < dependency.Length; j++) {
4958 if (!(tableList.Contains(dependency[j].Table))) {
4959 return false;
4965 return true;
4969 public void WriteXmlSchema(XmlWriter writer) {
4970 WriteXmlSchema(writer, false);
4973 public void WriteXmlSchema(XmlWriter writer, bool writeHierarchy)
4975 IntPtr hscp;
4976 Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXmlSchema|API> %d#\n", ObjectID);
4977 try{
4978 if (this.tableName.Length == 0) {
4979 throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
4982 if (!CheckForClosureOnExpressions(this, writeHierarchy)) {
4983 throw ExceptionBuilder.CanNotSerializeDataTableHierarchy();
4986 DataSet ds = null;
4987 string tablenamespace = this.tableNamespace;//SQL BU Defect Tracking 286968
4989 // Generate SchemaTree and write it out
4990 if (null == this.DataSet) {
4991 ds = new DataSet();
4992 // if user set values on DataTable, it isn't necessary
4993 // to set them on the DataSet because they won't be inherited
4994 // but it is simpler to set them in both places
4996 // if user did not set values on DataTable, it is required
4997 // to set them on the DataSet so the table will inherit
4998 // the value already on the Datatable
4999 ds.SetLocaleValue(_culture, _cultureUserSet);
5000 ds.CaseSensitive = this.CaseSensitive;
5001 ds.Namespace = this.Namespace;
5002 ds.RemotingFormat = this.RemotingFormat;
5003 ds.Tables.Add(this);
5006 if (writer != null) {
5007 XmlTreeGen treeGen = new XmlTreeGen(SchemaFormat.Public);
5008 treeGen.Save(null, this, writer, writeHierarchy);
5010 if (null != ds) {
5011 ds.Tables.Remove(this);
5012 this.tableNamespace = tablenamespace;
5015 finally {
5016 Bid.ScopeLeave(ref hscp);
5020 [ResourceExposure(ResourceScope.Machine)]
5021 [ResourceConsumption(ResourceScope.Machine)]
5022 public void WriteXmlSchema(String fileName) {
5023 WriteXmlSchema(fileName, false);
5026 [ResourceExposure(ResourceScope.Machine)]
5027 [ResourceConsumption(ResourceScope.Machine)]
5028 public void WriteXmlSchema(String fileName, bool writeHierarchy)
5030 XmlTextWriter xw = new XmlTextWriter( fileName, null );
5031 try {
5032 xw.Formatting = Formatting.Indented;
5033 xw.WriteStartDocument(true);
5034 WriteXmlSchema(xw, writeHierarchy);
5035 xw.WriteEndDocument();
5037 finally {
5038 xw.Close();
5042 public XmlReadMode ReadXml(Stream stream)
5044 if (stream == null)
5045 return XmlReadMode.Auto;
5047 XmlTextReader xr = new XmlTextReader(stream);
5049 // Prevent Dtd entity in DataTable
5050 xr.XmlResolver = null;
5052 return ReadXml(xr, false);
5055 public XmlReadMode ReadXml(TextReader reader)
5057 if (reader == null)
5058 return XmlReadMode.Auto;
5060 XmlTextReader xr = new XmlTextReader(reader);
5062 // Prevent Dtd entity in DataTable
5063 xr.XmlResolver = null;
5065 return ReadXml(xr, false);
5068 [ResourceExposure(ResourceScope.Machine)]
5069 [ResourceConsumption(ResourceScope.Machine)]
5070 public XmlReadMode ReadXml(string fileName)
5072 XmlTextReader xr = new XmlTextReader(fileName);
5074 // Prevent Dtd entity in DataTable
5075 xr.XmlResolver = null;
5079 return ReadXml( xr , false);
5081 finally {
5082 xr.Close();
5086 public XmlReadMode ReadXml(XmlReader reader)
5088 return ReadXml(reader, false);
5091 private void RestoreConstraint(bool originalEnforceConstraint) {
5092 if (this.DataSet != null) {
5093 this.DataSet.EnforceConstraints = originalEnforceConstraint;
5095 else {
5096 this.EnforceConstraints = originalEnforceConstraint;
5100 private bool IsEmptyXml(XmlReader reader) {
5101 if (reader.IsEmptyElement) {
5102 if (reader.AttributeCount == 0 || (reader.LocalName == Keywords.DIFFGRAM && reader.NamespaceURI == Keywords.DFFNS)) {
5103 return true;
5105 if (reader.AttributeCount == 1) {
5106 reader.MoveToAttribute(0);
5107 if ((this.Namespace == reader.Value) &&
5108 (this.Prefix == reader.LocalName) &&
5109 (reader.Prefix == Keywords.XMLNS) &&
5110 (reader.NamespaceURI == Keywords.XSD_XMLNS_NS))
5111 return true;
5114 return false;
5117 internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
5119 IntPtr hscp;
5120 Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXml|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
5121 try {
5123 DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
5124 try {
5125 bool fDataFound = false;
5126 bool fSchemaFound = false;
5127 bool fDiffsFound = false;
5128 bool fIsXdr = false;
5129 int iCurrentDepth = -1;
5130 XmlReadMode ret = XmlReadMode.Auto;
5132 // clear the hashtable to avoid conflicts between diffgrams, SqlHotFix 782
5133 rowDiffIdUsage.Prepare(this);
5135 if (reader == null)
5136 return ret;
5137 bool originalEnforceConstraint = false;
5138 if (this.DataSet != null) {
5139 originalEnforceConstraint = this.DataSet.EnforceConstraints;
5140 this.DataSet.EnforceConstraints = false;
5142 else {
5143 originalEnforceConstraint = this.EnforceConstraints;
5144 this.EnforceConstraints = false;
5147 if (reader is XmlTextReader)
5148 ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
5150 XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5151 XmlDataLoader xmlload = null;
5154 reader.MoveToContent();
5155 if (Columns.Count == 0) {
5156 if (IsEmptyXml(reader)) {
5157 reader.Read();
5158 return ret;
5162 if (reader.NodeType == XmlNodeType.Element) {
5163 iCurrentDepth = reader.Depth;
5165 if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5166 if (Columns.Count == 0) {
5167 if (reader.IsEmptyElement) {
5168 reader.Read();
5169 return XmlReadMode.DiffGram;
5171 throw ExceptionBuilder.DataTableInferenceNotSupported();
5173 this.ReadXmlDiffgram(reader);
5174 // read the closing tag of the current element
5175 ReadEndElement(reader);
5177 RestoreConstraint(originalEnforceConstraint);
5178 return XmlReadMode.DiffGram;
5181 // if reader points to the schema load it
5182 if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5183 // load XDR schema and exit
5184 ReadXDRSchema(reader);
5186 RestoreConstraint(originalEnforceConstraint);
5187 return XmlReadMode.ReadSchema; //since the top level element is a schema return
5190 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5191 // load XSD schema and exit
5192 ReadXmlSchema(reader, denyResolving);
5193 RestoreConstraint(originalEnforceConstraint);
5194 return XmlReadMode.ReadSchema; //since the top level element is a schema return
5197 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
5198 if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5199 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5201 else {
5202 this.enforceConstraints = originalEnforceConstraint;
5205 throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5208 // now either the top level node is a table and we load it through dataReader...
5210 // ... or backup the top node and all its attributes because we may need to InferSchema
5211 XmlElement topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5212 if (reader.HasAttributes) {
5213 int attrCount = reader.AttributeCount;
5214 for (int i=0;i<attrCount;i++) {
5215 reader.MoveToAttribute(i);
5216 if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
5217 topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
5218 else {
5219 XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
5220 attr.Prefix = reader.Prefix;
5221 attr.Value = reader.GetAttribute(i);
5225 reader.Read();
5227 while(MoveToElement(reader, iCurrentDepth)) {
5229 if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5230 this.ReadXmlDiffgram(reader);
5231 // read the closing tag of the current element
5232 ReadEndElement(reader);
5233 RestoreConstraint(originalEnforceConstraint);
5234 return XmlReadMode.DiffGram;
5237 // if reader points to the schema load it...
5240 if (!fSchemaFound && !fDataFound && reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5241 // load XDR schema and exit
5242 ReadXDRSchema(reader);
5243 fSchemaFound = true;
5244 fIsXdr = true;
5245 continue;
5248 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5249 // load XSD schema and exit
5250 ReadXmlSchema(reader, denyResolving);
5251 fSchemaFound = true;
5252 continue;
5255 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
5256 if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5257 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5259 else {
5260 this.enforceConstraints = originalEnforceConstraint;
5262 throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5265 if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5266 this.ReadXmlDiffgram(reader);
5267 fDiffsFound = true;
5268 ret = XmlReadMode.DiffGram;
5270 else {
5271 // we found data here
5272 fDataFound = true;
5274 if (!fSchemaFound && Columns.Count == 0) {
5275 XmlNode node = xdoc.ReadNode(reader);
5276 topNode.AppendChild(node);
5278 else {
5279 if (xmlload == null)
5280 xmlload = new XmlDataLoader(this, fIsXdr, topNode, false);
5281 xmlload.LoadData(reader);
5282 if (fSchemaFound)
5283 ret = XmlReadMode.ReadSchema;
5284 else
5285 ret = XmlReadMode.IgnoreSchema;
5290 // read the closing tag of the current element
5291 ReadEndElement(reader);
5293 // now top node contains the data part
5294 xdoc.AppendChild(topNode);
5296 if (!fSchemaFound && Columns.Count == 0) {
5297 if (IsEmptyXml(reader)) {
5298 reader.Read();
5299 return ret;
5301 throw ExceptionBuilder.DataTableInferenceNotSupported();
5304 if (xmlload == null)
5305 xmlload = new XmlDataLoader(this, fIsXdr, false);
5307 // so we InferSchema
5308 if (!fDiffsFound) {// we need to add support for inference here
5311 RestoreConstraint(originalEnforceConstraint);
5312 return ret;
5314 finally {
5315 rowDiffIdUsage.Cleanup();
5318 finally{
5319 Bid.ScopeLeave(ref hscp);
5323 internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolving)
5325 DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
5326 try {
5327 bool fSchemaFound = false;
5328 bool fDataFound = false;
5329 bool fIsXdr = false;
5330 int iCurrentDepth = -1;
5331 XmlReadMode ret = mode;
5333 // Dev11 904428: prepare and cleanup rowDiffId hashtable
5334 rowDiffIdUsage.Prepare(this);
5336 if (reader == null)
5337 return ret;
5339 bool originalEnforceConstraint = false;
5340 if (this.DataSet != null) {
5341 originalEnforceConstraint = this.DataSet.EnforceConstraints;
5342 this.DataSet.EnforceConstraints = false;
5344 else {
5345 originalEnforceConstraint = this.EnforceConstraints;
5346 this.EnforceConstraints = false;
5349 if (reader is XmlTextReader)
5350 ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
5352 XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5354 if ((mode != XmlReadMode.Fragment) && (reader.NodeType == XmlNodeType.Element))
5355 iCurrentDepth = reader.Depth;
5357 reader.MoveToContent();
5358 if (Columns.Count == 0) {
5359 if (IsEmptyXml(reader)) {
5360 reader.Read();
5361 return ret;
5365 XmlDataLoader xmlload = null;
5367 if (reader.NodeType == XmlNodeType.Element) {
5368 XmlElement topNode = null;
5369 if (mode == XmlReadMode.Fragment) {
5370 xdoc.AppendChild(xdoc.CreateElement("ds_sqlXmlWraPPeR"));
5371 topNode = xdoc.DocumentElement;
5373 else { //handle the top node
5374 if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5375 if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
5376 if (Columns.Count == 0) {
5377 if(reader.IsEmptyElement) {
5378 reader.Read();
5379 return XmlReadMode.DiffGram;
5381 throw ExceptionBuilder.DataTableInferenceNotSupported();
5383 this.ReadXmlDiffgram(reader);
5384 // read the closing tag of the current element
5385 ReadEndElement(reader);
5387 else {
5388 reader.Skip();
5390 RestoreConstraint(originalEnforceConstraint);
5391 return ret;
5394 if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5395 // load XDR schema and exit
5396 if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
5397 ReadXDRSchema(reader);
5399 else {
5400 reader.Skip();
5402 RestoreConstraint(originalEnforceConstraint);
5403 return ret; //since the top level element is a schema return
5406 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5407 // load XSD schema and exit
5408 if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
5409 ReadXmlSchema(reader, denyResolving);
5411 else
5412 reader.Skip();
5413 RestoreConstraint(originalEnforceConstraint);
5414 return ret; //since the top level element is a schema return
5417 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
5418 if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5419 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5421 else {
5422 this.enforceConstraints = originalEnforceConstraint;
5424 throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5427 // now either the top level node is a table and we load it through dataReader...
5428 // ... or backup the top node and all its attributes
5429 topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5430 if (reader.HasAttributes) {
5431 int attrCount = reader.AttributeCount;
5432 for (int i=0;i<attrCount;i++) {
5433 reader.MoveToAttribute(i);
5434 if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
5435 topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
5436 else {
5437 XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
5438 attr.Prefix = reader.Prefix;
5439 attr.Value = reader.GetAttribute(i);
5443 reader.Read();
5446 while(MoveToElement(reader, iCurrentDepth)) {
5448 if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5449 // load XDR schema
5450 if (!fSchemaFound && !fDataFound && (mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
5451 ReadXDRSchema(reader);
5452 fSchemaFound = true;
5453 fIsXdr = true;
5455 else {
5456 reader.Skip();
5458 continue;
5461 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5462 // load XSD schema and exit
5463 if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
5464 ReadXmlSchema(reader, denyResolving);
5465 fSchemaFound = true;
5467 else {
5468 reader.Skip();
5470 continue;
5473 if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5474 if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
5475 if (Columns.Count == 0) {
5476 if(reader.IsEmptyElement) {
5477 reader.Read();
5478 return XmlReadMode.DiffGram;
5480 throw ExceptionBuilder.DataTableInferenceNotSupported();
5482 this.ReadXmlDiffgram(reader);
5483 ret = XmlReadMode.DiffGram;
5485 else {
5486 reader.Skip();
5488 continue;
5491 if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
5492 if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5493 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5495 else {
5496 this.enforceConstraints = originalEnforceConstraint;
5498 throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5501 if (mode == XmlReadMode.DiffGram) {
5502 reader.Skip();
5503 continue; // we do not read data in diffgram mode
5506 // if we are here we found some data
5507 fDataFound = true;
5509 if (mode == XmlReadMode.InferSchema) { //save the node in DOM until the end;
5510 XmlNode node = xdoc.ReadNode(reader);
5511 topNode.AppendChild(node);
5513 else {
5514 if (Columns.Count == 0) {
5515 throw ExceptionBuilder.DataTableInferenceNotSupported();
5517 if (xmlload == null)
5518 xmlload = new XmlDataLoader(this, fIsXdr, topNode, mode == XmlReadMode.IgnoreSchema);
5519 xmlload.LoadData(reader);
5521 } //end of the while
5523 // read the closing tag of the current element
5524 ReadEndElement(reader);
5526 // now top node contains the data part
5527 xdoc.AppendChild(topNode);
5529 if (xmlload == null)
5530 xmlload = new XmlDataLoader(this, fIsXdr, mode == XmlReadMode.IgnoreSchema);
5532 if (mode == XmlReadMode.DiffGram) {
5533 // we already got the diffs through XmlReader interface
5534 RestoreConstraint(originalEnforceConstraint);
5535 return ret;
5537 //todo
5538 // Load Data
5539 if (mode == XmlReadMode.InferSchema) {
5540 if (Columns.Count == 0) {
5541 throw ExceptionBuilder.DataTableInferenceNotSupported();
5544 // Microsoft xmlload.InferSchema(xdoc, null);
5545 // Microsoft xmlload.LoadData(xdoc);
5548 RestoreConstraint(originalEnforceConstraint);
5550 return ret;
5552 finally {
5553 // Dev11 904428: prepare and cleanup rowDiffId hashtable
5554 rowDiffIdUsage.Cleanup();
5559 internal void ReadEndElement(XmlReader reader) {
5560 while (reader.NodeType == XmlNodeType.Whitespace) {
5561 reader.Skip();
5563 if (reader.NodeType == XmlNodeType.None) {
5564 reader.Skip();
5566 else if (reader.NodeType == XmlNodeType.EndElement) {
5567 reader.ReadEndElement();
5570 internal void ReadXDRSchema(XmlReader reader) {
5571 XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5572 XmlNode schNode = xdoc.ReadNode(reader);;
5573 //consume and ignore it - No support
5576 internal bool MoveToElement(XmlReader reader, int depth) {
5577 while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.Element && reader.Depth > depth) {
5578 reader.Read();
5580 return (reader.NodeType == XmlNodeType.Element);
5582 private void ReadXmlDiffgram(XmlReader reader) { // fill correctly
5583 int d = reader.Depth;
5584 bool fEnforce = this.EnforceConstraints;
5585 this.EnforceConstraints =false;
5586 DataTable newDt;
5587 bool isEmpty;
5589 if (this.Rows.Count == 0) {
5590 isEmpty = true;
5591 newDt = this;
5593 else {
5594 isEmpty = false;
5595 newDt = this.Clone();
5596 newDt.EnforceConstraints = false;
5599 newDt.Rows.nullInList = 0;
5601 reader.MoveToContent();
5603 if ((reader.LocalName != Keywords.DIFFGRAM) && (reader.NamespaceURI != Keywords.DFFNS))
5604 return;
5605 reader.Read();
5606 if (reader.NodeType == XmlNodeType.Whitespace) {
5607 MoveToElement(reader, reader.Depth - 1 /*iCurrentDepth*/); // skip over whitespaces.
5610 newDt.fInLoadDiffgram = true;
5612 if (reader.Depth > d) {
5613 if ((reader.NamespaceURI != Keywords.DFFNS) && (reader.NamespaceURI != Keywords.MSDNS)) {
5614 //we should be inside the dataset part
5615 XmlDocument xdoc = new XmlDocument();
5616 XmlElement node = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5617 reader.Read();
5618 if (reader.Depth-1 > d) {
5619 XmlDataLoader xmlload = new XmlDataLoader(newDt, false, node, false);
5620 xmlload.isDiffgram = true; // turn on the special processing
5621 xmlload.LoadData(reader);
5623 ReadEndElement(reader);
5625 if (((reader.LocalName == Keywords.SQL_BEFORE) && (reader.NamespaceURI == Keywords.DFFNS)) ||
5626 ((reader.LocalName == Keywords.MSD_ERRORS) && (reader.NamespaceURI == Keywords.DFFNS)))
5629 //this will consume the changes and the errors part
5630 XMLDiffLoader diffLoader = new XMLDiffLoader();
5631 diffLoader.LoadDiffGram(newDt, reader);
5634 // get to the closing diff tag
5635 while(reader.Depth > d) {
5636 reader.Read();
5638 // read the closing tag
5639 ReadEndElement(reader);
5642 if (newDt.Rows.nullInList > 0)
5643 throw ExceptionBuilder.RowInsertMissing(newDt.TableName);
5646 newDt.fInLoadDiffgram = false;
5647 List<DataTable> tableList = new List<DataTable>();
5648 tableList.Add(this);
5649 CreateTableList(this, tableList);
5651 // this is terrible, optimize it
5652 for (int i = 0; i < tableList.Count ; i++) {
5653 DataRelation[] relations = tableList[i].NestedParentRelations;
5654 foreach(DataRelation rel in relations) {
5655 if (rel != null && rel.ParentTable == tableList[i]) {
5656 foreach (DataRow r in tableList[i].Rows) {
5657 foreach (DataRelation rel2 in relations) {
5658 r.CheckForLoops(rel2);
5665 if (!isEmpty) {
5666 this.Merge(newDt);
5668 this.EnforceConstraints = fEnforce;
5671 internal void ReadXSDSchema(XmlReader reader, bool denyResolving) {
5672 XmlSchemaSet sSet = new XmlSchemaSet();
5673 while (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5674 XmlSchema s = XmlSchema.Read(reader, null);
5675 sSet.Add(s);
5676 //read the end tag
5677 ReadEndElement(reader);
5679 sSet.Compile();
5681 XSDSchema schema = new XSDSchema();
5682 schema.LoadSchema(sSet, this);
5686 public void ReadXmlSchema(Stream stream)
5688 if (stream == null)
5689 return;
5691 ReadXmlSchema( new XmlTextReader( stream ), false );
5694 public void ReadXmlSchema(TextReader reader)
5696 if (reader == null)
5697 return;
5699 ReadXmlSchema( new XmlTextReader( reader ), false );
5702 [ResourceExposure(ResourceScope.Machine)]
5703 [ResourceConsumption(ResourceScope.Machine)]
5704 public void ReadXmlSchema(String fileName)
5706 XmlTextReader xr = new XmlTextReader(fileName);
5707 try {
5708 ReadXmlSchema( xr, false );
5710 finally {
5711 xr.Close();
5715 public void ReadXmlSchema(XmlReader reader)
5717 ReadXmlSchema(reader, false);
5720 internal void ReadXmlSchema(XmlReader reader, bool denyResolving)
5722 IntPtr hscp;
5723 Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXmlSchema|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
5724 try{
5725 DataSet ds = new DataSet();
5726 SerializationFormat cachedRemotingFormat = this.RemotingFormat;
5727 // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
5728 ds.ReadXmlSchema(reader, denyResolving);
5730 string CurrentTableFullName = ds.MainTableName;
5732 if (Common.ADP.IsEmpty(this.tableName) && Common.ADP.IsEmpty(CurrentTableFullName))
5733 return;
5735 DataTable currentTable = null;
5737 if (!Common.ADP.IsEmpty(this.tableName)) {
5738 if (!Common.ADP.IsEmpty(this.Namespace)) {
5739 currentTable = ds.Tables[this.tableName, this.Namespace];
5741 else {//SQL BU defect tracking 240293
5742 int tableIndex = ds.Tables.InternalIndexOf(this.tableName);
5743 if (tableIndex > -1) {
5744 currentTable = ds.Tables[tableIndex];
5748 else{ //!Common.ADP.IsEmpty(CurrentTableFullName)
5749 string CurrentTableNamespace = "";
5750 int nsSeperator = CurrentTableFullName.IndexOf(':');
5751 if (nsSeperator > -1) {
5752 CurrentTableNamespace = CurrentTableFullName.Substring(0, nsSeperator);
5754 string CurrentTableName = CurrentTableFullName.Substring(nsSeperator + 1, CurrentTableFullName.Length - nsSeperator -1);
5756 currentTable = ds.Tables[CurrentTableName, CurrentTableNamespace];
5759 if (currentTable == null) { // bug fix :99186
5760 string qTableName = string.Empty;
5761 if (!Common.ADP.IsEmpty(this.tableName)) {
5762 qTableName = (this.Namespace.Length > 0)? (this.Namespace + ":" + this.tableName):this.tableName;
5764 else {
5765 qTableName = CurrentTableFullName ;
5767 throw ExceptionBuilder.TableNotFound(qTableName);
5770 currentTable._remotingFormat = cachedRemotingFormat;
5772 List<DataTable> tableList = new List<DataTable>();
5773 tableList.Add(currentTable);
5774 CreateTableList(currentTable, tableList);
5775 List<DataRelation> relationList = new List<DataRelation>();
5776 CreateRelationList(tableList, relationList);
5778 if (relationList.Count == 0) {
5779 if (this.Columns.Count == 0) {
5780 DataTable tempTable = currentTable;
5781 if (tempTable != null)
5782 tempTable.CloneTo(this, null, false); // we may have issue Amir
5783 if (this.DataSet == null && this.tableNamespace == null) { // webdata 105506
5784 // for standalone table, clone wont get these correctly, since they may come with inheritance
5785 this.tableNamespace = tempTable.Namespace;
5788 return;
5790 else {
5791 if (Common.ADP.IsEmpty(this.TableName)) {
5792 this.TableName = currentTable.TableName;
5793 if (!Common.ADP.IsEmpty(currentTable.Namespace)) {
5794 this.Namespace = currentTable.Namespace;
5797 if (this.DataSet == null) {
5798 DataSet dataset = new DataSet(ds.DataSetName);
5799 // webdata 105506
5800 // if user set values on DataTable, it isn't necessary
5801 // to set them on the DataSet because they won't be inherited
5802 // but it is simpler to set them in both places
5804 // if user did not set values on DataTable, it is required
5805 // to set them on the DataSet so the table will inherit
5806 // the value already on the Datatable
5807 dataset.SetLocaleValue(ds.Locale, ds.ShouldSerializeLocale());
5808 dataset.CaseSensitive = ds.CaseSensitive;
5809 dataset.Namespace = ds.Namespace;
5810 dataset.mainTableName = ds.mainTableName;
5811 dataset.RemotingFormat = ds.RemotingFormat;
5813 dataset.Tables.Add(this);
5816 DataTable targetTable = CloneHierarchy(currentTable, this.DataSet, null);
5818 foreach(DataTable tempTable in tableList) {
5819 DataTable destinationTable = this.DataSet.Tables[tempTable.tableName, tempTable.Namespace];
5820 DataTable sourceTable = ds.Tables[tempTable.tableName, tempTable.Namespace];
5821 foreach(Constraint tempConstrain in sourceTable.Constraints) {
5822 ForeignKeyConstraint fkc = tempConstrain as ForeignKeyConstraint; // we have already cloned the UKC when cloning the datatable
5823 if (fkc != null) {
5824 if (fkc.Table != fkc.RelatedTable) {
5825 if (tableList.Contains(fkc.Table) && tableList.Contains(fkc.RelatedTable)) {
5826 ForeignKeyConstraint newFKC = (ForeignKeyConstraint)fkc.Clone(destinationTable.DataSet);
5827 if (!destinationTable.Constraints.Contains(newFKC.ConstraintName))
5828 destinationTable.Constraints.Add(newFKC); // we know that the dest table is already in the table
5834 foreach(DataRelation rel in relationList) {
5835 if (!this.DataSet.Relations.Contains(rel.RelationName))
5836 this.DataSet.Relations.Add(rel.Clone(this.DataSet));
5839 bool hasExternaldependency = false;
5841 foreach(DataTable tempTable in tableList) {
5842 foreach(DataColumn dc in tempTable.Columns) {
5843 hasExternaldependency = false;
5844 if (dc.Expression.Length != 0) {
5845 DataColumn[] dependency = dc.DataExpression.GetDependency();
5846 for (int j = 0; j < dependency.Length; j++) {
5847 if (!tableList.Contains(dependency[j].Table)) {
5848 hasExternaldependency = true;
5849 break;
5853 if (!hasExternaldependency) {
5854 this.DataSet.Tables[tempTable.TableName, tempTable.Namespace].Columns[dc.ColumnName].Expression = dc.Expression;
5857 hasExternaldependency = false;
5862 finally{
5863 Bid.ScopeLeave(ref hscp);
5867 private void CreateTableList(DataTable currentTable, List<DataTable> tableList) {
5868 foreach( DataRelation r in currentTable.ChildRelations ) {
5869 if (! tableList.Contains(r.ChildTable)) {
5870 tableList.Add(r.ChildTable);
5871 CreateTableList(r.ChildTable, tableList);
5875 private void CreateRelationList(List<DataTable> tableList, List<DataRelation> relationList) {
5876 foreach(DataTable table in tableList) {
5877 foreach( DataRelation r in table.ChildRelations) {
5878 if (tableList.Contains(r.ChildTable) && tableList.Contains(r.ParentTable)) {
5879 relationList.Add(r);
5885 /**************************************************************************
5886 The V2.0 (no V1.0 or V1.1) WSDL for Untyped DataTable being returned as a result (no parameters)
5887 <s:element name="anyUserSpecifiedMethodName">
5888 <!-- This is where parameters go -->
5889 <s:complexType />
5890 </s:element>
5891 <s:element name="anyUserSpecifiedMethodName"+"Response">
5892 <s:complexType>
5893 <s:sequence>
5894 <s:element minOccurs="0" maxOccurs="1" name="anyUserSpecifiedMethodName"+"Result">
5895 <s:complexType>
5896 <s:sequence>
5897 <s:any minOccurs="0" maxOccurs="unbounded" namespace="http://www.w3.org/2001/XMLSchema" processContents="lax" />
5898 <s:any minOccurs="1" namespace="urn:schemas-microsoft-com:xml-diffgram-v1" processContents="lax" />
5899 </s:sequence>
5900 </s:complexType>
5901 </s:element>
5902 </s:sequence>
5903 </s:complexType>
5904 </s:element>
5906 Typed DataTable is not supported in WSDL (SQLBU 444636)
5908 either fails because xsd generates its typed DataTable with an internal parameterless ctor
5910 or System.NullReferenceException: Object reference not set to an instance of an object. (if namespace of StronglyTyped DataTable is not set)
5911 at System.Data.XmlTreeGen.FindTargetNamespace(DataTable table)
5913 or System.InvalidOperationException: Schema Id is missing. The schema returned from WebServiceDataSetServer.Service+StudentsDataTable.GetSchema() must have an Id.
5914 at System.Xml.Serialization.SerializableMapping.RetrieveSerializableSchema()
5915 *****************************************************************************/
5916 public static XmlSchemaComplexType GetDataTableSchema(XmlSchemaSet schemaSet) {
5917 XmlSchemaComplexType type = new XmlSchemaComplexType();
5918 XmlSchemaSequence sequence = new XmlSchemaSequence();
5919 XmlSchemaAny any = new XmlSchemaAny();
5920 any.Namespace = XmlSchema.Namespace;
5921 any.MinOccurs = 0;
5922 any.MaxOccurs = Decimal.MaxValue;
5923 any.ProcessContents = XmlSchemaContentProcessing.Lax;
5924 sequence.Items.Add(any);
5926 any = new XmlSchemaAny();
5927 any.Namespace = Keywords.DFFNS;
5928 any.MinOccurs = 1; // when recognizing WSDL - MinOccurs="0" denotes DataSet, a MinOccurs="1" for DataTable
5929 any.ProcessContents = XmlSchemaContentProcessing.Lax;
5930 sequence.Items.Add(any);
5932 type.Particle = sequence;
5934 return type;
5937 XmlSchema IXmlSerializable.GetSchema() {
5938 return GetSchema();
5941 protected virtual XmlSchema GetSchema() {
5942 if (GetType() == typeof(DataTable)) {
5943 return null;
5945 MemoryStream stream = new MemoryStream();
5947 XmlWriter writer = new XmlTextWriter(stream, null);
5948 if (writer != null) {
5949 (new XmlTreeGen(SchemaFormat.WebService)).Save(this, writer);
5951 stream.Position = 0;
5952 return XmlSchema.Read(new XmlTextReader(stream), null);
5955 void IXmlSerializable.ReadXml(XmlReader reader) {
5956 IXmlTextParser textReader = reader as IXmlTextParser;
5957 bool fNormalization = true;
5958 if (textReader != null) {
5959 fNormalization = textReader.Normalized;
5960 textReader.Normalized = false;
5962 ReadXmlSerializable(reader);
5964 if (textReader != null) {
5965 textReader.Normalized = fNormalization;
5969 void IXmlSerializable.WriteXml(XmlWriter writer) {
5970 WriteXmlSchema(writer, false);
5971 WriteXml(writer, XmlWriteMode.DiffGram, false);
5974 protected virtual void ReadXmlSerializable(XmlReader reader) {
5975 ReadXml(reader, XmlReadMode.DiffGram, true);
5980 DefaultValue(false),
5981 ResCategoryAttribute(Res.DataCategory_Data),
5982 ResDescriptionAttribute(Res.DataTableSerializeHierarchy)
5984 public bool SerializeHierarchy {
5985 get {
5986 return this.serializeHierarchy;
5988 set {
5989 this.serializeHierarchy = value;
5994 // RowDiffIdUsageSection & DSRowDiffIdUsageSection Usage:
5996 // DataTable.[DS]RowDiffIdUsageSection rowDiffIdUsage = new DataTable.[DS]RowDiffIdUsageSection();
5997 // try {
5998 // rowDiffIdUsage.Prepare(DataTable or DataSet, depending on type);
6000 // // code that requires RowDiffId usage
6002 // }
6003 // finally {
6004 // rowDiffIdUsage.Cleanup();
6005 // }
6007 // Nested calls are allowed on different tables. For example, user can register to row change events and trigger
6008 // ReadXml on different table/ds). But, if user code will try to call ReadXml on table that is already in the scope,
6009 // this code will assert since nested calls on same table are unsupported.
6010 internal struct RowDiffIdUsageSection
6012 #if DEBUG
6013 // This list contains tables currently used in diffgram processing, not including new tables that might be added later during.
6014 // if diffgram processing is not started, this value must be null. when it starts, relevant method should call Prepare.
6015 // Notes:
6016 // * in case of ReadXml on empty DataSet, this list can be initialized as empty (so empty list != null).
6017 // * only one scope is allowed on single thread, either for datatable or dataset
6018 // * assert is triggered if same table is added to this list twice
6020 // do not allocate TLS data in RETAIL bits!
6021 [ThreadStatic]
6022 internal static List<DataTable> s_usedTables;
6023 #endif //DEBUG
6025 DataTable _targetTable;
6027 internal void Prepare(DataTable table)
6029 Debug.Assert(_targetTable == null, "do not reuse this section");
6030 Debug.Assert(table != null);
6031 Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
6032 #if DEBUG
6033 Debug.Assert(s_usedTables == null || !s_usedTables.Contains(table),
6034 "Nested call with same table can cause data corruption!");
6035 #endif //DEBUG
6037 #if DEBUG
6038 if (s_usedTables == null)
6039 s_usedTables = new List<DataTable>();
6040 s_usedTables.Add(table);
6041 #endif //DEBUG
6042 _targetTable = table;
6043 table.rowDiffId = null;
6046 [Conditional("DEBUG")]
6047 internal void Cleanup()
6049 // cannot assume target table was set - ThreadAbortException can be raised before Start is called
6050 if (_targetTable != null)
6052 #if DEBUG
6053 Debug.Assert(s_usedTables != null && s_usedTables.Contains(_targetTable), "missing Prepare before Cleanup");
6054 if (s_usedTables != null)
6056 s_usedTables.Remove(_targetTable);
6057 if (s_usedTables.Count == 0)
6058 s_usedTables = null;
6060 #endif //DEBUG
6061 _targetTable.rowDiffId = null;
6065 [Conditional("DEBUG")]
6066 internal static void Assert(string message)
6068 #if DEBUG
6069 // this code asserts scope was created, but it does not assert that the table was included in it
6070 // note that in case of DataSet, new tables might be added to the list in which case they won't appear in s_usedTables.
6071 Debug.Assert(s_usedTables != null, message);
6072 #endif //DEBUG
6076 internal struct DSRowDiffIdUsageSection
6078 DataSet _targetDS;
6080 internal void Prepare(DataSet ds)
6082 Debug.Assert(_targetDS == null, "do not reuse this section");
6083 Debug.Assert(ds != null);
6085 _targetDS = ds;
6086 #if DEBUG
6087 // initialize list of tables out of current tables
6088 // note: it might remain empty (still initialization is needed for assert to operate)
6089 if (RowDiffIdUsageSection.s_usedTables == null)
6090 RowDiffIdUsageSection.s_usedTables = new List<DataTable>();
6091 #endif //DEBUG
6092 for (int tableIndex = 0; tableIndex < ds.Tables.Count; ++tableIndex)
6094 DataTable table = ds.Tables[tableIndex];
6095 #if DEBUG
6096 Debug.Assert(!RowDiffIdUsageSection.s_usedTables.Contains(table), "Nested call with same table can cause data corruption!");
6097 RowDiffIdUsageSection.s_usedTables.Add(table);
6098 #endif //DEBUG
6099 Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
6100 table.rowDiffId = null;
6104 [Conditional("DEBUG")]
6105 internal void Cleanup()
6107 // cannot assume target was set - ThreadAbortException can be raised before Start is called
6108 if (_targetDS != null)
6110 #if DEBUG
6111 Debug.Assert(RowDiffIdUsageSection.s_usedTables != null, "missing Prepare before Cleanup");
6112 #endif //DEBUG
6114 for (int tableIndex = 0; tableIndex < _targetDS.Tables.Count; ++tableIndex)
6116 DataTable table = _targetDS.Tables[tableIndex];
6117 #if DEBUG
6118 // cannot assert that table exists in the usedTables - new tables might be
6119 // created during diffgram processing in DataSet.ReadXml.
6120 if (RowDiffIdUsageSection.s_usedTables != null)
6121 RowDiffIdUsageSection.s_usedTables.Remove(table);
6122 #endif //DEBUG
6123 table.rowDiffId = null;
6125 #if DEBUG
6126 if (RowDiffIdUsageSection.s_usedTables != null && RowDiffIdUsageSection.s_usedTables.Count == 0)
6127 RowDiffIdUsageSection.s_usedTables = null; // out-of-scope
6128 #endif //DEBUG
6133 internal Hashtable RowDiffId {
6134 get {
6135 // assert scope has been created either with RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare
6136 RowDiffIdUsageSection.Assert("missing call to RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare");
6138 if (rowDiffId == null)
6139 rowDiffId = new Hashtable();
6140 return rowDiffId;
6144 internal int ObjectID {
6145 get {
6146 return _objectID;
6150 internal void AddDependentColumn(DataColumn expressionColumn) {
6151 if (dependentColumns == null)
6152 dependentColumns = new List<DataColumn>();
6154 if (!dependentColumns.Contains(expressionColumn)) {
6155 // only remember unique columns but expect non-unique columns to be added
6156 dependentColumns.Add(expressionColumn);
6160 internal void RemoveDependentColumn(DataColumn expressionColumn) {
6161 if (dependentColumns != null && dependentColumns.Contains(expressionColumn)) {
6162 dependentColumns.Remove(expressionColumn);
6166 internal void EvaluateExpressions() {
6167 //evaluates all expressions for all rows in table
6168 // SQLBU 414992: Serious performance issue when calling Merge
6169 // this improves performance by only computing expressions when they are present
6170 // and iterating over the rows instead of computing their position multiple times
6171 if ((null != dependentColumns) && (0 < dependentColumns.Count)) {
6172 foreach(DataRow row in Rows) {
6173 // only evaluate original values if different from current.
6174 if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6175 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, null);
6177 if (row.newRecord != -1) {
6178 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, null);
6180 if (row.tempRecord != -1) {
6181 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, null);
6187 internal void EvaluateExpressions(DataRow row, DataRowAction action, List<DataRow> cachedRows) {
6188 // evaluate all expressions for specified row
6189 if (action == DataRowAction.Add ||
6190 action == DataRowAction.Change||
6191 (action == DataRowAction.Rollback && (row.oldRecord!=-1 || row.newRecord!=-1))) {
6192 // only evaluate original values if different from current.
6193 if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6194 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, cachedRows);
6196 if (row.newRecord != -1) {
6197 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, cachedRows);
6199 if (row.tempRecord != -1) {
6200 EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, cachedRows);
6202 return;
6204 else if ((action == DataRowAction.Delete || (action==DataRowAction.Rollback && row.oldRecord==-1 && row.newRecord==-1)) && dependentColumns != null) {
6205 foreach(DataColumn col in dependentColumns) {
6206 if (col.DataExpression != null && col.DataExpression.HasLocalAggregate() && col.Table == this) {
6207 for (int j = 0; j < Rows.Count; j++) {
6208 DataRow tableRow = Rows[j];
6210 if (tableRow.oldRecord != -1 && tableRow.oldRecord != tableRow.newRecord) {
6211 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Original, null);
6214 for (int j = 0; j < Rows.Count; j++)
6216 DataRow tableRow = Rows[j];
6218 if (tableRow.tempRecord != -1)
6220 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Proposed, null);
6223 // VSTFDEVDIV911434: Order is important here - we need to update proposed before current
6224 // Oherwise rows that are in edit state will get ListChanged/PropertyChanged event before default value is changed
6225 // It is also the reason why we are not doping it in the single loop: EvaluateDependentExpression can update the
6226 // whole table, if it happens, current for all but first row is updated before proposed value
6227 for (int j = 0; j < Rows.Count; j++)
6229 DataRow tableRow = Rows[j];
6231 if (tableRow.newRecord != -1)
6233 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Current, null);
6236 break;
6240 if (cachedRows != null) {
6241 foreach (DataRow relatedRow in cachedRows) {
6242 if (relatedRow.oldRecord != -1 && relatedRow.oldRecord != relatedRow.newRecord) {
6243 relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Original, null);
6245 if (relatedRow.newRecord != -1) {
6246 relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Current, null);
6248 if (relatedRow.tempRecord != -1) {
6249 relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Proposed, null);
6256 internal void EvaluateExpressions(DataColumn column) {
6257 // evaluates all rows for expression from specified column
6258 Debug.Assert(column.Computed, "Only computed columns should be re-evaluated.");
6259 int count = column.table.Rows.Count;
6260 if (column.DataExpression.IsTableAggregate() && count > 0) {
6261 // this value is a constant across the table.
6262 object aggCurrent = column.DataExpression.Evaluate();
6263 for (int j = 0; j < count; j++) {
6264 DataRow row = column.table.Rows[j];
6265 // only evaluate original values if different from current.
6266 if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6267 column[row.oldRecord] = aggCurrent;
6269 if (row.newRecord != -1) {
6270 column[row.newRecord] = aggCurrent;
6272 if (row.tempRecord != -1) {
6273 column[row.tempRecord] = aggCurrent;
6277 else {
6278 for (int j = 0; j < count; j++) {
6279 DataRow row = column.table.Rows[j];
6281 if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6282 column[row.oldRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Original);
6284 if (row.newRecord != -1) {
6285 column[row.newRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Current);
6287 if (row.tempRecord != -1) {
6288 column[row.tempRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Proposed);
6293 // SQLBU 501916 - DataTable internal index is corrupted:'5'
6294 column.Table.ResetInternalIndexes(column);
6295 EvaluateDependentExpressions(column);
6298 internal void EvaluateDependentExpressions(DataColumn column) {
6299 // DataTable.Clear(), DataRowCollection.Clear() & DataColumn.set_Expression
6300 if (column.dependentColumns != null) {
6301 foreach (DataColumn dc in column.dependentColumns) {
6302 if ((dc.table != null) && !Object.ReferenceEquals(column, dc)) { // SQLBU 502736
6303 EvaluateExpressions(dc);
6309 internal void EvaluateDependentExpressions(List<DataColumn> columns, DataRow row, DataRowVersion version, List<DataRow> cachedRows) {
6310 if (columns == null)
6311 return;
6312 //Expression evaluation is done first over same table expressions.
6313 int count = columns.Count;
6314 for(int i = 0; i < count; i++) {
6315 if (columns[i].Table == this) {// if this column is in my table
6316 DataColumn dc = columns[i];
6317 if (dc.DataExpression != null && dc.DataExpression.HasLocalAggregate()) {
6318 // if column expression references a local Table aggregate we need to recalc it for the each row in the local table
6319 DataRowVersion expressionVersion = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
6320 bool isConst = dc.DataExpression.IsTableAggregate(); //is expression constant for entire table?
6321 object newValue = null;
6322 if (isConst) { //if new value, just compute once
6323 newValue = dc.DataExpression.Evaluate(row, expressionVersion);
6325 for (int j = 0; j < Rows.Count; j++) { //evaluate for all rows in the table
6326 DataRow dr = Rows[j];
6327 if (dr.RowState == DataRowState.Deleted) {
6328 continue;
6330 else if (expressionVersion == DataRowVersion.Original && (dr.oldRecord == -1 || dr.oldRecord == dr.newRecord)) {
6331 continue;
6334 if (!isConst) {
6335 newValue = dc.DataExpression.Evaluate(dr, expressionVersion);
6337 SilentlySetValue(dr, dc, expressionVersion, newValue);
6340 else {
6341 if (row.RowState == DataRowState.Deleted) {
6342 continue;
6344 else if (version == DataRowVersion.Original && (row.oldRecord == -1 || row.oldRecord == row.newRecord)) {
6345 continue;
6347 SilentlySetValue(row, dc, version, dc.DataExpression == null ? dc.DefaultValue : dc.DataExpression.Evaluate(row, version));
6351 // now do expression evaluation for expression columns other tables.
6352 count = columns.Count;
6353 for(int i = 0; i < count; i++) {
6354 DataColumn dc = columns[i];
6355 // if this column is NOT in my table or it is in the table and is not a local aggregate (self refs)
6356 if (dc.Table != this || (dc.DataExpression != null && !dc.DataExpression.HasLocalAggregate())) {
6357 DataRowVersion foreignVer = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
6359 // first - evaluate expressions for cachedRows (deletes & updates)
6360 if (cachedRows != null) {
6361 foreach (DataRow cachedRow in cachedRows) {
6362 if (cachedRow.Table != dc.Table)
6363 continue;
6364 // don't update original version if child row doesn't have an oldRecord.
6365 if (foreignVer == DataRowVersion.Original && cachedRow.newRecord == cachedRow.oldRecord)
6366 continue;
6367 if (cachedRow != null && ((cachedRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || cachedRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
6368 object newValue = dc.DataExpression.Evaluate(cachedRow, foreignVer);
6369 SilentlySetValue(cachedRow, dc, foreignVer, newValue);
6374 // next check parent relations
6375 for (int j = 0; j < ParentRelations.Count; j++) {
6376 DataRelation relation = ParentRelations[j];
6377 if (relation.ParentTable != dc.Table)
6378 continue;
6379 foreach (DataRow parentRow in row.GetParentRows(relation, version)) {
6380 if (cachedRows != null && cachedRows.Contains(parentRow))
6381 continue;
6382 // don't update original version if child row doesn't have an oldRecord.
6383 if (foreignVer == DataRowVersion.Original && parentRow.newRecord == parentRow.oldRecord)
6384 continue;
6385 if (parentRow != null && ((parentRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || parentRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
6386 object newValue = dc.DataExpression.Evaluate(parentRow, foreignVer);
6387 SilentlySetValue(parentRow, dc, foreignVer, newValue);
6391 // next check child relations
6392 for (int j = 0; j < ChildRelations.Count; j++) {
6393 DataRelation relation = ChildRelations[j];
6394 if (relation.ChildTable != dc.Table)
6395 continue;
6396 foreach (DataRow childRow in row.GetChildRows(relation, version)) {
6397 // don't update original version if child row doesn't have an oldRecord.
6398 if (cachedRows != null && cachedRows.Contains(childRow))
6399 continue;
6400 if (foreignVer == DataRowVersion.Original && childRow.newRecord == childRow.oldRecord)
6401 continue;
6402 if (childRow != null && ((childRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || childRow.oldRecord != -1))) { // if deleted GetRecordFromVersion will throw
6403 object newValue = dc.DataExpression.Evaluate(childRow, foreignVer);
6404 SilentlySetValue(childRow, dc, foreignVer, newValue);