1 //------------------------------------------------------------------------------
2 // <copyright file="DataTable.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System
.Data
{
11 using System
.Collections
;
12 using System
.Collections
.Generic
;
13 using System
.ComponentModel
;
14 using System
.Diagnostics
;
15 using System
.Globalization
;
17 using System
.Runtime
.Serialization
;
19 using System
.Threading
;
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
;
28 /// <para>Represents one table of in-memory data.</para>
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"),
39 public class DataTable
: MarshalByValueComponent
, System
.ComponentModel
.IListSource
, ISupportInitializeNotification
, ISerializable
, IXmlSerializable
{
40 private DataSet dataSet
;
41 private DataView defaultView
= null;
45 /// Monotonically increasing number representing the order <see cref="DataRow"/> have been added to <see cref="DataRowCollection"/>.
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
;
52 internal readonly DataColumnCollection columnCollection
;
55 private readonly ConstraintCollection constraintCollection
;
57 //SimpleContent implementation
58 private int elementColumnCount
= 0;
61 internal DataRelationCollection parentRelationsCollection
;
62 internal DataRelationCollection childRelationsCollection
;
65 internal readonly RecordManager recordManager
;
68 internal readonly List
<Index
> indexes
;
70 private List
<Index
> shadowIndexes
;
71 private int shadowCount
;
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
;
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;
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;
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
;
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
);
180 /// <para>Initializes a new instance of the <see cref='System.Data.DataTable'/> class with no arguments.</para>
183 GC
.SuppressFinalize(this);
184 Bid
.Trace("<ds.DataTable.DataTable|API> %d#\n", ObjectID
);
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);
198 /// <para>Intitalizes a new instance of the <see cref='System.Data.DataTable'/> class with the specified table
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()) {
217 case "DataTable.RemotingFormat" : //DataTable.RemotingFormat does not exist in V1/V1.1 versions
218 remotingFormat
= (SerializationFormat
)e
.Value
;
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
);
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");
265 fCreatedDataSet
= true;
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);
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
);
288 DeserializeTableData(info
, context
, 0);
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]);
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
);
335 info
.AddValue("DataTable.ExtendedProperties", ExtendedProperties
);
338 info
.AddValue("DataTable.Columns.Count", Columns
.Count
);
340 //Check for closure of expression in case of single table.
342 List
<DataTable
> list
= new List
<DataTable
>();
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
);
375 info
.AddValue(String
.Format(formatProvider
, "DataTable.DataColumn_{0}.Expression", i
), Columns
[i
].Expression
);
379 info
.AddValue(String
.Format(formatProvider
, "DataTable.DataColumn_{0}.ExtendedProperties", i
), Columns
[i
].extendedProperties
);
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");
415 extendedProperties
= (PropertyCollection
) info
.GetValue("DataTable.ExtendedProperties", typeof(PropertyCollection
));
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));
452 expressions
[i
] = info
.GetString(String
.Format(formatProvider
, "DataTable.DataColumn_{0}.Expression", i
));
456 dc
.extendedProperties
= (PropertyCollection
) info
.GetValue(String
.Format(formatProvider
, "DataTable.DataColumn_{0}.ExtendedProperties", i
), typeof(PropertyCollection
));
460 for(int i
= 0; i
< colCount
; i
++) {
461 if (expressions
[i
] != null) {
462 Columns
[i
].Expression
= expressions
[i
];
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
;
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();
499 list
.Add(uc
.ConstraintName
);
501 list
.Add(uc
.IsPrimaryKey
);
502 list
.Add(uc
.ExtendedProperties
);
504 constraintList
.Add(list
);
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();
525 list
.Add(fk
.ConstraintName
);
526 list
.Add(parentInfo
);
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.
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];
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]];
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
;
638 case DataRowState
.Unchanged
:
639 //rowStates[bitIndex] = false;
640 //rowStates[bitIndex + 1] = false;
642 case DataRowState
.Added
:
643 //rowStates[bitIndex] = false;
644 rowStates
[bitIndex
+ 1] = true;
646 case DataRowState
.Modified
:
647 rowStates
[bitIndex
] = true;
648 //rowStates[bitIndex + 1] = false;
651 case DataRowState
.Deleted
:
652 rowStates
[bitIndex
] = true;
653 rowStates
[bitIndex
+ 1] = true;
656 throw ExceptionBuilder
.InvalidRowState(rowState
);
658 if (-1 != row
.tempRecord
) {
659 rowStates
[bitIndex
+ 2] = true;
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
;
707 enforceConstraints
= false;
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.
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.
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
;
744 case DataRowState
.Added
:
746 row
.newRecord
= recordIndex
;
749 case DataRowState
.Modified
:
750 row
.oldRecord
= recordIndex
;
751 row
.newRecord
= recordIndex
+ 1;
752 rowArr
[recordIndex
+ 1] = row
;
755 case DataRowState
.Deleted
:
756 row
.oldRecord
= recordIndex
;
761 if (rowStates
[bitIndex
+ 2]) {
762 row
.tempRecord
= recordIndex
;
763 rowArr
[recordIndex
] = row
;
769 row
.rowID
= nextRowID
;
771 ConvertToRowError(i
, rowErrors
, colErrors
);
773 recordManager
.SetRowCache(rowArr
);
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];
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
;
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
];
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
]);
851 /// <para>Indicates whether string comparisons within the table are case-sensitive.</para>
853 [ResDescriptionAttribute(Res
.DataTableCaseSensitiveDescr
)]
854 public bool CaseSensitive
{
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
;
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;
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()
897 if (forceReset
|| ndx
.HasRemoteAggregate
) {
898 ndx
.Reset(); // resets & fires
901 ndx
.FireResetEvent(); // fire the Reset event we were firing
905 if (!ADP
.IsCatchableExceptionType (e
)) {
908 ExceptionBuilder
.TraceExceptionWithoutRethrow(e
);
919 RestoreShadowIndexes();
925 internal void SuspendIndexEvents() {
926 Bid
.Trace("<ds.DataTable.SuspendIndexEvents|Info> %d#, %d\n", ObjectID
, _suspendIndexEvents
);
927 _suspendIndexEvents
++;
931 public bool IsInitialized
{
933 return !fInitInProgress
;
937 private bool IsTypedDataTable
{
939 switch (_isTypedDataTable
) {
941 _isTypedDataTable
= (byte)((this.GetType() != typeof(DataTable
))? 1 : 2);
942 return (1 == _isTypedDataTable
);
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
;
958 _compareFlags
= CompareOptions
.IgnoreCase
| CompareOptions
.IgnoreKanaType
| CompareOptions
.IgnoreWidth
;
962 foreach (Constraint constraint
in Constraints
) {
963 constraint
.CheckConstraint();
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
{
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) {
997 /* internal bool SelfNestedWithOneRelation {
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
{
1007 if (!AreIndexEventsSuspended
) {
1008 for (int i
= indexes
.Count
-1; 0 <= i
; --i
) {
1009 Index index
= indexes
[i
];
1010 if (index
.RefCount
<= 1) {
1020 DefaultValue(SerializationFormat
.Xml
)
1022 public SerializationFormat RemotingFormat
{
1024 return _remotingFormat
;
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
{
1041 return ukColumnPositionForInference
;
1044 ukColumnPositionForInference
= value;
1049 /// <para>Gets the collection of child relations for this <see cref='System.Data.DataTable'/>.</para>
1053 ResDescriptionAttribute(Res
.DataTableChildRelationsDescr
),
1054 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)
1056 public DataRelationCollection ChildRelations
{
1058 if (childRelationsCollection
== null)
1059 childRelationsCollection
= new DataRelationCollection
.DataTableRelationCollection(this, false);
1060 return childRelationsCollection
;
1065 /// <para>Gets the collection of columns that belong to this table.</para>
1068 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
1069 ResCategoryAttribute(Res
.DataCategory_Data
),
1070 ResDescriptionAttribute(Res
.DataTableColumnsDescr
)
1072 public DataColumnCollection Columns
{
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
1084 private CompareInfo CompareInfo
{
1086 if (null == _compareInfo
) {
1087 _compareInfo
= Locale
.CompareInfo
;
1089 return _compareInfo
;
1094 /// <para>Gets the collection of constraints maintained by this table.</para>
1097 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
1098 ResCategoryAttribute(Res
.DataCategory_Data
),
1099 ResDescriptionAttribute(Res
.DataTableConstraintsDescr
)
1101 public ConstraintCollection Constraints
{
1103 return constraintCollection
;
1109 /// Resets the <see cref='System.Data.DataTable.Constraints'/> property to its default state.
1112 private void ResetConstraints() {
1113 Constraints
.Clear();
1117 /// <para>Gets the <see cref='System.Data.DataSet'/> that this table belongs to.</para>
1119 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
), Browsable(false), ResDescriptionAttribute(Res
.DataTableDataSetDescr
)]
1120 public DataSet DataSet
{
1127 /// Internal method for setting the DataSet pointer.
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) {
1141 //Set the remoting format variable directly
1142 if (dataSet
!= null) {
1143 _remotingFormat
= dataSet
.RemotingFormat
;
1149 /// <para>Gets a customized view of the table which may include a
1150 /// filtered view, or a cursor position.</para>
1152 [Browsable(false), ResDescriptionAttribute(Res
.DataTableDefaultViewDescr
)]
1153 public DataView DefaultView
{
1155 DataView view
= defaultView
;
1157 if (null != dataSet
) {
1158 view
= dataSet
.DefaultViewManager
.CreateDataView(this);
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);
1175 /// <para>Gets or sets the expression that will return a value used to represent
1176 /// this table in UI.</para>
1180 ResCategoryAttribute(Res
.DataCategory_Data
),
1181 ResDescriptionAttribute(Res
.DataTableDisplayExpressionDescr
)
1183 public string DisplayExpression
{
1185 return DisplayExpressionInternal
;
1188 if (value != null && value.Length
> 0) {
1189 this.displayExpression
= new DataExpression(this, value);
1192 this.displayExpression
= null;
1196 internal string DisplayExpressionInternal
{
1198 return(displayExpression
!= null ? displayExpression
.Expression
: "");
1202 internal bool EnforceConstraints
{
1204 if (SuspendEnforceConstraints
) {
1207 if (dataSet
!= null)
1208 return dataSet
.EnforceConstraints
;
1210 return this.enforceConstraints
;
1213 if (dataSet
== null && this.enforceConstraints
!= value) {
1215 EnableConstraints();
1217 this.enforceConstraints
= value;
1222 internal bool SuspendEnforceConstraints
{
1224 return _suspendEnforceConstraints
;
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();
1250 this.EnforceConstraints
= false;
1251 throw ExceptionBuilder
.EnforceConstraint();
1256 /// <para>Gets the collection of customized user information.</para>
1259 ResCategoryAttribute(Res
.DataCategory_Data
),
1261 ResDescriptionAttribute(Res
.ExtendedPropertiesDescr
)
1263 public PropertyCollection ExtendedProperties
{
1265 if (extendedProperties
== null) {
1266 extendedProperties
= new PropertyCollection();
1268 return extendedProperties
;
1272 internal IFormatProvider FormatProvider
{
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
;
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>
1291 [Browsable(false), ResDescriptionAttribute(Res
.DataTableHasErrorsDescr
)]
1292 public bool HasErrors
{
1294 for (int i
= 0; i
< Rows
.Count
; i
++) {
1295 if (Rows
[i
].HasErrors
) {
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>
1308 [ResDescriptionAttribute(Res
.DataTableLocaleDescr
)]
1309 public CultureInfo Locale
{
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");
1318 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.set_Locale|API> %d#\n", ObjectID
);
1320 bool userSet
= true;
1321 if (null == value) {
1322 // reset Locale to inherit from DataSet
1324 value = (null != dataSet
) ? dataSet
.Locale
: _culture
;
1326 if (_culture
!= value && !_culture
.Equals(value)) {
1328 bool exceptionThrown
= false;
1329 CultureInfo oldLocale
= _culture
;
1330 bool oldUserSet
= _cultureUserSet
;
1332 _cultureUserSet
= true;
1333 SetLocaleValue(value, true, false);
1334 if ((null == DataSet
) || DataSet
.ValidateLocaleConstraint()) {
1336 SetLocaleValue(value, true, true);
1341 exceptionThrown
= true;
1345 if (!flag
) { // reset old locale if ValidationFailed or exception thrown
1347 SetLocaleValue(oldLocale
, true, true);
1349 catch(Exception e
) { // failed to reset all indexes for all constraints
1350 if (!Common
.ADP
.IsCatchableExceptionType(e
)) {
1353 Common
.ADP
.TraceExceptionWithoutRethrow(e
);
1355 _cultureUserSet
= oldUserSet
;
1356 if (!exceptionThrown
) {
1357 throw ExceptionBuilder
.CannotChangeCaseLocale(null);
1361 SetLocaleValue(value, true, true);
1363 _cultureUserSet
= userSet
;
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
))) {
1375 _compareInfo
= null;
1376 _formatProvider
= null;
1377 _hashCodeProvider
= null;
1379 foreach(DataColumn column
in Columns
) {
1380 column
._hashCode
= GetSpecialHashCode(column
.ColumnName
);
1384 foreach (Constraint constraint
in Constraints
) {
1385 constraint
.CheckConstraint();
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
;
1404 /// <para>Gets or sets the initial starting size for this table.</para>
1408 ResCategoryAttribute(Res
.DataCategory_Data
),
1409 ResDescriptionAttribute(Res
.DataTableMinimumCapacityDescr
)
1411 public int MinimumCapacity
{
1413 return recordManager
.MinimumCapacity
;
1416 if (value != recordManager
.MinimumCapacity
) {
1417 recordManager
.MinimumCapacity
= value;
1422 internal int RecordCapacity
{
1424 return recordManager
.RecordCapacity
;
1429 internal int ElementColumnCount
{
1431 return elementColumnCount
;
1434 if ((value > 0) && (xmlText
!= null))
1435 throw ExceptionBuilder
.TableCannotAddToSimpleContent();
1436 else elementColumnCount
= value;
1441 /// <para>Gets the collection of parent relations for this <see cref='System.Data.DataTable'/>.</para>
1445 ResDescriptionAttribute(Res
.DataTableParentRelationsDescr
),
1446 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)
1448 public DataRelationCollection ParentRelations
{
1450 if (parentRelationsCollection
== null)
1451 parentRelationsCollection
= new DataRelationCollection
.DataTableRelationCollection(this, true);
1452 return parentRelationsCollection
;
1456 internal bool MergingData
{
1461 mergingData
= value;
1465 internal DataRelation
[] NestedParentRelations
{
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");
1476 return _nestedParentRelations
;
1480 internal bool SchemaLoading
{
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
{
1511 foreach(DataRelation relation
in this.ParentRelations
) {
1520 /// <para>Gets or sets an array of columns that function as primary keys for the data
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
{
1531 UniqueConstraint primayKeyConstraint
= primaryKey
;
1532 if (null != primayKeyConstraint
) {
1533 Debug
.Assert(2 <= primaryKey
.ConstraintIndex
.RefCount
, "bad primaryKey index RefCount");
1534 return primayKeyConstraint
.Key
.ToArray();
1539 UniqueConstraint key
= null;
1540 UniqueConstraint existingKey
= null;
1542 // Loading with persisted property
1543 if (fInitInProgress
&& value != null) {
1544 delayedSetPrimaryKey
= value;
1548 if ((value != null) && (value.Length
!= 0)) {
1550 for (int i
= 0; i
< value.Length
; i
++) {
1551 if (value[i
] != null)
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
)))
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);
1579 UniqueConstraint oldKey
= primaryKey
;
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();
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
);
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;
1621 /// Indicates whether the <see cref='System.Data.DataTable.PrimaryKey'/> property should be persisted.
1624 private bool ShouldSerializePrimaryKey() {
1625 return(primaryKey
!= null);
1630 /// Resets the <see cref='System.Data.DataTable.PrimaryKey'/> property to its default state.
1633 private void ResetPrimaryKey() {
1638 /// <para>Gets the collection of rows that belong to this table.</para>
1640 [Browsable(false), ResDescriptionAttribute(Res
.DataTableRowsDescr
)]
1641 public DataRowCollection Rows
{
1643 return rowCollection
;
1648 /// <para>Gets or sets the name of the table.</para>
1651 RefreshProperties(RefreshProperties
.All
),
1653 ResCategoryAttribute(Res
.DataCategory_Data
),
1654 ResDescriptionAttribute(Res
.DataTableTableNameDescr
)
1656 public string TableName
{
1662 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.set_TableName|API> %d#, value='%ls'\n", ObjectID
, value);
1664 if (value == null) {
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
);
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");
1700 encodedTableName
= null;
1702 else if (String
.Compare(tableName
, value, false, currentLocale
) != 0) {
1703 RaisePropertyChanging("TableName");
1705 encodedTableName
= null;
1709 Bid
.ScopeLeave(ref hscp
);
1715 internal string EncodedTableName
{
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
1739 while(j
< nestedRelations
.Length
&&((nestedRelations
[j
].ParentTable
== this)||(visitedTables
.Contains(nestedRelations
[j
].ParentTable
)))) {
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
1749 if (DataSet
!= null) { // if it cant return from parent tables, return NS from dataset, if exists
1750 return DataSet
.Namespace
;
1753 return string.Empty
;
1760 /// Gets or sets the namespace for the <see cref='System.Data.DataTable'/>.
1764 ResCategoryAttribute(Res
.DataCategory_Data
),
1765 ResDescriptionAttribute(Res
.DataTableNamespaceDescr
)
1767 public string Namespace
{
1769 if (tableNamespace
== null) {
1770 return GetInheritedNamespace(new List
<DataTable
>());
1772 return tableNamespace
;
1776 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.set_Namespace|API> %d#, value='%ls'\n", ObjectID
, value);
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;
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
) {
1819 if (realNamespace
!= null) {
1820 rel
.ChildTable
.CheckNamespaceValidityForNestedParentRelations(realNamespace
, this);
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
){
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();
1860 /// Indicates whether the <see cref='System.Data.DataTable.Namespace'/> property should be persisted.
1863 private bool ShouldSerializeNamespace() {
1864 return(tableNamespace
!= null);
1869 /// Resets the <see cref='System.Data.DataTable.Namespace'/> property to its default state.
1872 private void ResetNamespace() {
1873 this.Namespace
= null;
1877 /// <para>[To be supplied.]</para>
1879 virtual public void BeginInit() {
1880 fInitInProgress
= true;
1884 /// <para>[To be supplied.]</para>
1886 virtual public void EndInit() {
1887 if (dataSet
== null || !dataSet
.fInitInProgress
) {
1888 Columns
.FinishInitCollection();
1889 Constraints
.FinishInitConstraints();
1890 foreach(DataColumn dc
in Columns
){
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
) {
1905 delayedViews
.Clear();
1911 /// <para>[To be supplied.]</para>
1915 ResCategoryAttribute(Res
.DataCategory_Data
),
1916 ResDescriptionAttribute(Res
.DataTablePrefixDescr
)
1918 public string Prefix
{
1919 get { return tablePrefix;}
1921 if (value == null) {
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
{
1939 if (xmlText
!= value) {
1940 if (xmlText
!= null) {
1941 if (value != null) {
1942 throw ExceptionBuilder
.MultipleTextOnlyColumns();
1944 Columns
.Remove(xmlText
);
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
])
1957 internal decimal MaxOccurs
{
1966 internal decimal MinOccurs
{
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
);
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
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
);
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
);
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);
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;
2033 DataRowState saveRowState
= targetRow
.RowState
;
2034 int saveIdxRecord
= (saveRowState
== DataRowState
.Added
) ? targetRow
.newRecord
: saveIdxRecord
= targetRow
.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
;
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
);
2058 saveIdxRecord
= ((saveRowState
== DataRowState
.Added
) ? newRecord
: oldRecord
);
2060 SetMergeRecords(targetRow
, newRecord
, oldRecord
, (newRecord
== -1) ? DataRowAction
.Delete
: DataRowAction
.Change
);
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)
2080 Debug
.Assert(saveIdxRecord
== ((saveRowState
== DataRowState
.Added
) ? targetRow
.newRecord
: targetRow
.oldRecord
), "oops, you change index record without noticing it");
2083 targetRow
.tempRecord
= proposedRecord
;
2088 if (row
.HasErrors
) {
2089 if (targetRow
.RowError
.Length
== 0) {
2090 targetRow
.RowError
= row
.RowError
;
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
]));
2101 if (!preserveChanges
) {
2102 targetRow
.ClearErrors();
2110 /// <para>Commits all the changes made to this table since the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
2112 public void AcceptChanges() {
2114 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.AcceptChanges|API> %d#\n", ObjectID
);
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();
2123 for (int i
= 0; i
< oldRows
.Length
; ++i
) {
2124 if (oldRows
[i
].rowID
!= -1) {
2125 oldRows
[i
].AcceptChanges();
2130 RestoreIndexEvents(false);
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() {
2148 internal DataTable
Clone(DataSet cloneDS
) {
2150 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.Clone|INFO> %d#, cloneDS=%d\n", ObjectID
, (cloneDS
!= null) ? cloneDS
.ObjectID
: 0);
2152 DataTable clone
= CreateInstance();
2153 if (clone
.Columns
.Count
> 0) // Microsoft : To clean up all the schema in strong typed dataset.
2155 return CloneTo(clone
, cloneDS
, false);
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());
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
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;
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
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
];
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
];
2314 public DataTable
Copy(){
2316 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.Copy|API> %d#\n", ObjectID
);
2318 DataTable destTable
= this.Clone();
2320 foreach (DataRow row
in Rows
)
2321 CopyRow(destTable
, row
);
2326 Bid
.ScopeLeave(ref hscp
);
2331 /// <para>Occurs when a value has been submitted for this column.</para>
2333 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableColumnChangingDescr
)]
2334 public event DataColumnChangeEventHandler ColumnChanging
{
2336 Bid
.Trace("<ds.DataTable.add_ColumnChanging|API> %d#\n", ObjectID
);
2337 onColumnChangingDelegate
+= value;
2340 Bid
.Trace("<ds.DataTable.remove_ColumnChanging|API> %d#\n", ObjectID
);
2341 onColumnChangingDelegate
-= value;
2346 /// <para>[To be supplied.]</para>
2348 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableColumnChangedDescr
)]
2349 public event DataColumnChangeEventHandler ColumnChanged
{
2351 Bid
.Trace("<ds.DataTable.add_ColumnChanged|API> %d#\n", ObjectID
);
2352 onColumnChangedDelegate
+= value;
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
{
2366 onInitialized
+= value;
2369 onInitialized
-= value;
2373 internal event PropertyChangedEventHandler PropertyChanging
{
2375 Bid
.Trace("<ds.DataTable.add_PropertyChanging|INFO> %d#\n", ObjectID
);
2376 onPropertyChangingDelegate
+= value;
2379 Bid
.Trace("<ds.DataTable.remove_PropertyChanging|INFO> %d#\n", ObjectID
);
2380 onPropertyChangingDelegate
-= value;
2386 /// Occurs after a row in the table has been successfully edited.
2389 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableRowChangedDescr
)]
2390 public event DataRowChangeEventHandler RowChanged
{
2392 Bid
.Trace("<ds.DataTable.add_RowChanged|API> %d#\n", ObjectID
);
2393 onRowChangedDelegate
+= value;
2396 Bid
.Trace("<ds.DataTable.remove_RowChanged|API> %d#\n", ObjectID
);
2397 onRowChangedDelegate
-= value;
2403 /// Occurs when the <see cref='System.Data.DataRow'/> is changing.
2406 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableRowChangingDescr
)]
2407 public event DataRowChangeEventHandler RowChanging
{
2409 Bid
.Trace("<ds.DataTable.add_RowChanging|API> %d#\n", ObjectID
);
2410 onRowChangingDelegate
+= value;
2413 Bid
.Trace("<ds.DataTable.remove_RowChanging|API> %d#\n", ObjectID
);
2414 onRowChangingDelegate
-= value;
2420 /// Occurs before a row in the table is
2421 /// about to be deleted.
2424 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableRowDeletingDescr
)]
2425 public event DataRowChangeEventHandler RowDeleting
{
2427 Bid
.Trace("<ds.DataTable.add_RowDeleting|API> %d#\n", ObjectID
);
2428 onRowDeletingDelegate
+= value;
2431 Bid
.Trace("<ds.DataTable.remove_RowDeleting|API> %d#\n", ObjectID
);
2432 onRowDeletingDelegate
-= value;
2438 /// Occurs after a row in the
2439 /// table has been deleted.
2442 [ResCategoryAttribute(Res
.DataCategory_Data
), ResDescriptionAttribute(Res
.DataTableRowDeletedDescr
)]
2443 public event DataRowChangeEventHandler RowDeleted
{
2445 Bid
.Trace("<ds.DataTable.add_RowDeleted|API> %d#\n", ObjectID
);
2446 onRowDeletedDelegate
+= value;
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
{
2457 Bid
.Trace("<ds.DataTable.add_TableClearing|API> %d#\n", ObjectID
);
2458 onTableClearingDelegate
+= value;
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
{
2469 Bid
.Trace("<ds.DataTable.add_TableCleared|API> %d#\n", ObjectID
);
2470 onTableClearedDelegate
+= value;
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
{
2481 onTableNewRowDelegate
+= value;
2484 onTableNewRowDelegate
-= value;
2488 [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
2489 public override ISite Site
{
2494 ISite oldSite
= Site
;
2495 if (value == null && oldSite
!= null) {
2496 IContainer cont
= oldSite
.Container
;
2499 for (int i
= 0; i
< Columns
.Count
; i
++) {
2500 if (Columns
[i
].Site
!= null) {
2501 cont
.Remove(Columns
[i
]);
2510 internal DataRow
AddRecords(int oldRecord
, int newRecord
) {
2512 if (oldRecord
== -1 && newRecord
== -1)
2519 row
= NewEmptyRow();
2520 row
.oldRecord
= oldRecord
;
2521 row
.newRecord
= newRecord
;
2527 internal void AddRow(DataRow row
) {
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;
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);
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)
2572 if (rollbackOnException
&& (nextRowID
== proposedID
+1)) {
2573 nextRowID
= proposedID
;
2576 row
.tempRecord
= record
;
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
);
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) {
2603 //throw ExceptionBuilder.ModifyingRow();
2609 /// Clears the table of all data.</para>
2612 public void Clear() {
2615 internal void Clear(bool clearAll
) {
2617 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.Clear|INFO> %d#, clearAll=%d{bool}\n", ObjectID
, clearAll
);
2620 Debug
.Assert(null == rowDiffId
, "wasn't previously cleared");
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);
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
) {
2648 row
.tempRecord
= -1;
2650 row
.RBTreeNodeId
= 0;
2656 if (shouldFireClearEvents
) {
2660 // SQLBU 501916 - DataTable internal index is corrupted:'5'
2661 foreach(DataColumn column
in Columns
) {
2662 EvaluateDependentExpressions(column
);
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
);
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
) {
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
2711 for (; leng2
> 0; leng2
--) {
2712 if (s2
[leng2
-1] != 0x20 && s2
[leng2
-1] != 0x3000)
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
);
2728 /// <para>Computes the given expression on the current rows that pass the filter criteria.</para>
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
{
2742 internal void CopyRow(DataTable table
, DataRow row
)
2744 int oldRecord
= -1, newRecord
= -1;
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
);
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
) {
2791 return FindRow(primaryKey
.Key
, values
);
2794 internal DataRow
FindByPrimaryKey(object value) {
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
);
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);
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() {
2835 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.GetChanges|API> %d#\n", ObjectID
);
2837 DataTable dtChanges
= this.Clone();
2840 for (int i
= 0; i
< Rows
.Count
; i
++) {
2842 if (row
.oldRecord
!= row
.newRecord
)
2843 dtChanges
.ImportRow(row
);
2846 if (dtChanges
.Rows
.Count
== 0)
2852 Bid
.ScopeLeave(ref hscp
);
2856 public DataTable
GetChanges(DataRowState rowStates
)
2859 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.GetChanges|API> %d#, rowStates=%d{ds.DataRowState}\n", ObjectID
, (int)rowStates
);
2861 DataTable dtChanges
= this.Clone();
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
++) {
2869 if ((row
.RowState
& rowStates
) != 0)
2870 dtChanges
.ImportRow(row
);
2873 if (dtChanges
.Rows
.Count
== 0)
2879 Bid
.ScopeLeave(ref hscp
);
2884 /// <para>Returns an array of <see cref='System.Data.DataRow'/> objects that contain errors.</para>
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
) {
2895 DataRow
[] temp
= NewRowArray(errorList
.Count
);
2896 errorList
.CopyTo(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);
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
)) {
2921 indexesLock
.ReleaseReaderLock();
2923 Index ndx
= new Index(this, indexDesc
, recordStates
, rowFilter
);
2928 IList System
.ComponentModel
.IListSource
.GetList() {
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
) {
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
);
2954 public void ImportRow(DataRow row
)
2957 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.ImportRow|API> %d#\n", ObjectID
);
2959 int oldRecord
= -1, newRecord
= -1;
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
);
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
]));
2991 Bid
.ScopeLeave(ref hscp
);
2996 internal void InsertRow(DataRow row
, long proposedID
) {
2999 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.InsertRow|INFO> %d#, row=%d\n", ObjectID
, row
.ObjectID
);
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
;
3026 drcevent
= RaiseRowChanging(null, row
, DataRowAction
.Add
, true);
3029 row
.tempRecord
= -1;
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
);
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
);
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();
3087 for (int i
= 0; i
< value.Length
; i
++) {
3088 if (null != value[i
]) {
3089 columnCollection
[i
][record
] = value[i
];
3092 columnCollection
[i
].Init(record
); // Increase AutoIncrementCurrent
3095 for (int i
= value.Length
; i
< colCount
; i
++) {
3096 columnCollection
[i
].Init(record
);
3100 catch (Exception e
) {
3102 if (Common
.ADP
.IsCatchableOrSecurityExceptionType (e
)) {
3103 FreeRecord(ref record
); // WebData 104246
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
);
3119 for (int i
= 0; i
< count
; ++i
) {
3120 columnCollection
[i
].Copy(sourceRecord
, record
);
3126 internal DataRow
NewEmptyRow() {
3127 rowBuilder
._record
= -1;
3128 DataRow dr
= NewRowFromBuilder( rowBuilder
);
3129 if (dataSet
!= null) {
3130 DataSet
.OnDataRowCreated( dr
);
3135 private DataRow
NewUninitializedRow() {
3136 DataRow dr
= NewRow(NewUninitializedRecord());
3141 /// <para>Creates a new <see cref='System.Data.DataRow'/>
3142 /// with the same schema as the table.</para>
3144 public DataRow
NewRow() {
3145 DataRow dr
= NewRow(-1);
3146 NewRowCreated(dr
); // this is the only API we want this event to be fired
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
;
3165 c
.Init(row
.tempRecord
);
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
) {
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
);
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
);
3200 /// <para>Gets the row type.</para>
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
) {
3211 if (null == EmptyDataRowArray
) {
3212 EmptyDataRowArray
= (DataRow
[]) Array
.CreateInstance(GetRowType(), 0);
3214 return EmptyDataRowArray
;
3216 return (DataRow
[]) Array
.CreateInstance(GetRowType(), size
);
3219 return ((0 == size
) ? DataTable
.zeroRows
: new DataRow
[size
]);
3223 internal bool NeedColumnChangeEvents
{
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
);
3258 /// <para>Notifies the <see cref='System.Data.DataTable'/> that a <see cref='System.Data.DataColumn'/> is
3259 /// being removed.</para>
3261 protected virtual void OnRemoveColumn(DataColumn column
) {
3264 private DataRowChangeEventArgs
OnRowChanged(DataRowChangeEventArgs args
, DataRow eRow
, DataRowAction eAction
) {
3265 if ((null != onRowChangedDelegate
) || IsTypedDataTable
) {
3267 args
= new DataRowChangeEventArgs(eRow
, eAction
);
3274 private DataRowChangeEventArgs
OnRowChanging(DataRowChangeEventArgs args
, DataRow eRow
, DataRowAction eAction
) {
3275 if ((null != onRowChangingDelegate
) || IsTypedDataTable
) {
3277 args
= new DataRowChangeEventArgs(eRow
, eAction
);
3279 OnRowChanging(args
);
3286 /// Raises the <see cref='System.Data.DataTable.RowChanged'/> event.
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
);
3299 /// Raises the <see cref='System.Data.DataTable.RowChanging'/> event.
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
);
3312 /// Raises the <see cref='System.Data.DataTable.OnRowDeleting'/> event.
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
);
3325 /// Raises the <see cref='System.Data.DataTable.OnRowDeleted'/> event.
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) {
3382 current
= current
.Substring(0, length
- 5).Trim();
3386 if (current
.StartsWith("[", StringComparison
.Ordinal
)) {
3387 if (current
.EndsWith("]", StringComparison
.Ordinal
)) {
3388 current
= current
.Substring(1, current
.Length
- 2);
3391 throw ExceptionBuilder
.InvalidSortString(split
[i
]);
3396 DataColumn column
= Columns
[current
];
3397 if(column
== null) {
3398 throw ExceptionBuilder
.ColumnOutOfRange(current
);
3400 indexDesc
[i
] = new IndexField(column
, descending);
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?
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
);
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
) {
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");
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
]);
3447 RestoreShadowIndexes();
3451 internal void RecordStateChanged(int record
, DataViewRowState oldState
, DataViewRowState newState
) {
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
);
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
) {
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
);
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
);
3508 positionIndexes
[indexCount
] = index
;
3509 indexes
[indexCount
].DeleteRecordFromIndex(index
); // this will delete the record from index and MUSt not fire event
3512 positionIndexes
[indexCount
] = -1; // this means record was not in index
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
);
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;
3555 equalValues
= dc
.CompareValueTo(record
, newValue
, true);
3558 // if expression has changed
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();
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>
3580 public void RejectChanges() {
3582 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.RejectChanges|API> %d#\n", ObjectID
);
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
]);
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
);
3617 if (oldRecord
== newRecord
) {
3621 RecordStateChanged(oldRecord
, oldRecordStatePre
, DataViewRowState
.None
,
3622 newRecord
, newRecordStatePre
, DataViewRowState
.None
);
3624 FreeRecord(ref oldRecord
);
3625 FreeRecord(ref newRecord
);
3628 Rows
.ArrayRemove(row
);
3631 // Resets the table back to its original state.
3632 public virtual void Reset() {
3634 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.Reset|API> %d#\n", ObjectID
);
3639 DataRelationCollection dr
= this.ParentRelations
;
3640 int count
= dr
.Count
;
3646 dr
= this.ChildRelations
;
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");
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
) {
3680 // SQLBU 501916: DataTable internal index is corrupted:'5'
3682 foreach(IndexField field
in ndx
.IndexFields
) {
3683 if (Object
.ReferenceEquals(column
, field
.Column
)) {
3697 RestoreShadowIndexes();
3701 internal void RollbackRow(DataRow row
) {
3703 SetNewRecord(row
, row
.oldRecord
, DataRowAction
.Rollback
, false, true);
3706 private DataRowChangeEventArgs
RaiseRowChanged(DataRowChangeEventArgs args
, DataRow eRow
, DataRowAction eAction
) {
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
))) {
3714 args
= new DataRowChangeEventArgs(eRow
, eAction
);
3719 catch (Exception f
) {
3721 if (!Common
.ADP
.IsCatchableExceptionType(f
)) {
3724 ExceptionBuilder
.TraceExceptionWithoutRethrow(f
);
3725 // ignore the exception
3730 private DataRowChangeEventArgs
RaiseRowChanging(DataRowChangeEventArgs args
, DataRow eRow
, DataRowAction eAction
) {
3731 if (UpdatingCurrent(eRow
, eAction
) && (IsTypedDataTable
|| (null != onRowChangingDelegate
))) {
3732 eRow
.inChangingEvent
= true;
3736 args
= OnRowChanging(args
, eRow
, eAction
);
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;
3748 args
= new DataRowChangeEventArgs(eRow
, eAction
);
3750 OnRowDeleting(args
);
3753 eRow
.inDeletingEvent
= false;
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.
3779 args
= RaiseRowChanging(args
, eRow
, eAction
);
3783 // cascade things...
3784 if (!MergingData
&& eAction
!= DataRowAction
.Nothing
&& eAction
!= DataRowAction
.ChangeOriginal
) {
3785 CascadeAll(eRow
, eAction
);
3792 /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects.</para>
3794 public DataRow
[] Select() {
3795 Bid
.Trace("<ds.DataTable.Select|API> %d#\n", ObjectID
);
3796 return new Select(this, "", "", DataViewRowState
.CurrentRows
).SelectRows();
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>
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();
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>
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();
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>
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
3858 CheckNotModifying(row
);
3860 if (proposedRecord
== row
.newRecord
) {
3862 Debug
.Assert(fireEvent
, "SetNewRecord is called with wrong parameter");
3863 RaiseRowChanged(null, row
, action
);
3868 Debug
.Assert(!row
.inChangingEvent
, "How can this row be in an infinite loop?");
3870 row
.tempRecord
= proposedRecord
;
3872 DataRowChangeEventArgs drcevent
= null;
3875 row
._action
= action
;
3876 drcevent
= RaiseRowChanging(null, row
, action
, fireEvent
);
3879 row
.tempRecord
= -1;
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 ?
3893 (row
.RowState
!= DataRowState
.Unchanged
?
3897 if (action
== DataRowAction
.Add
) { //if we come here from insert we do insert the row to collection
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
) {
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
) {
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
);
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) {
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
) {
4009 deferredException
= exc
;
4016 RaiseRowChanged(drcevent
, row
, action
);
4019 catch (Exception e
) {
4021 if (!Common
.ADP
.IsCatchableExceptionType(e
)) {
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
) {
4034 CheckNotModifying(row
);
4037 if (proposedRecord
== row
.oldRecord
) {
4041 int originalRecord
= row
.oldRecord
; // cache old record after potential RowChanging event
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
);
4069 row
.oldRecord
= proposedRecord
;
4070 if (proposedRecord
!= -1)
4071 this.recordManager
[proposedRecord
] = row
;
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");
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
;
4103 Debug
.Assert(1 <= shadowCount
, "unexpected negative shadow count");
4108 internal void ShadowIndexCopy(){
4109 if (shadowIndexes
== indexes
) {
4110 Debug
.Assert(0 < indexes
.Count
, "unexpected");
4111 shadowIndexes
= new List
<Index
>(indexes
);
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>
4118 public override string ToString() {
4119 if (this.displayExpression
== null)
4120 return this.TableName
;
4122 return this.TableName
+ " + " + this.DisplayExpressionInternal
;
4126 /// <para>[To be supplied.]</para>
4128 public void BeginLoadData() {
4130 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.BeginLoadData|API> %d#\n", ObjectID
);
4136 Debug
.Assert(null == loadIndex
, "loadIndex should already be null");
4138 // LoadDataRow may have been called before BeginLoadData and already
4139 // initialized loadIndexwithOriginalAdded & loadIndexwithCurrentDeleted
4141 initialLoad
= (Rows
.Count
== 0);
4143 SuspendIndexEvents();
4145 if (primaryKey
!= null) {
4146 loadIndex
= primaryKey
.Key
.GetSortIndex(DataViewRowState
.OriginalRows
);
4148 if(loadIndex
!= null) {
4153 if (DataSet
!= null) {
4154 savedEnforceConstraints
= DataSet
.EnforceConstraints
;
4155 DataSet
.EnforceConstraints
= false;
4158 this.EnforceConstraints
= false;
4162 Bid
.ScopeLeave(ref hscp
);
4167 /// <para>[To be supplied.]</para>
4169 public void EndLoadData() {
4171 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.EndLoadData|API> %d#\n", ObjectID
);
4176 if(loadIndex
!= null) {
4177 loadIndex
.RemoveRef();
4179 if (loadIndexwithOriginalAdded
!= null) {
4180 loadIndexwithOriginalAdded
.RemoveRef();
4182 if (loadIndexwithCurrentDeleted
!= null) {
4183 loadIndexwithCurrentDeleted
.RemoveRef();
4187 loadIndexwithOriginalAdded
= null;
4188 loadIndexwithCurrentDeleted
= null;
4192 RestoreIndexEvents(false);
4194 if (DataSet
!= null)
4195 DataSet
.EnforceConstraints
= savedEnforceConstraints
;
4197 this.EnforceConstraints
= true;
4200 Bid
.ScopeLeave(ref hscp
);
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>
4208 public DataRow
LoadDataRow(object[] values
, bool fAcceptChanges
) {
4210 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.LoadDataRow|API> %d#, fAcceptChanges=%d{bool}\n", ObjectID
, fAcceptChanges
);
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
);
4221 int resultRecord
= loadIndex
.GetRecord(result
);
4222 row
= recordManager
[resultRecord
];
4223 Debug
.Assert (row
!= null, "Row can't be null for index record");
4225 if (row
.RowState
== DataRowState
.Deleted
)
4226 SetNewRecord(row
, row
.oldRecord
, DataRowAction
.Rollback
, false, true);
4227 SetNewRecord(row
, record
, DataRowAction
.Change
, false, true);
4229 row
.AcceptChanges();
4233 row
= NewRow(record
);
4236 row
.AcceptChanges();
4240 // In case, BeginDataLoad is not called yet
4241 row
= UpdatingAdd(values
);
4243 row
.AcceptChanges();
4248 Bid
.ScopeLeave(ref hscp
);
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>
4256 public DataRow
LoadDataRow(object[] values
, LoadOption loadOption
) {
4258 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.LoadDataRow|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID
, (int)loadOption
);
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
4295 Bid
.ScopeLeave(ref hscp
);
4299 internal DataRow
UpdatingAdd(object[] values
) {
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
);
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
);
4316 DataRow row2
= NewRow(record
);
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)
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
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;
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
[] {
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
);
4383 internal void UpdatePropertyDescriptorCollectionCache() {
4384 propertyDescriptorCollectionCache
= null;
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.
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
{
4413 return ((typeName
== null) ? XmlQualifiedName
.Empty
: (XmlQualifiedName
)typeName
);
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
)
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
);
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
);
4447 throw Common
.ADP
.InvalidMissingSchemaAction(missingSchemaAction
);
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
){
4465 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.Load|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID
, (int)loadOption
);
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()) { //
4485 Bid
.ScopeLeave(ref hscp
);
4489 private DataRow
LoadRow(object[] values
, LoadOption loadOption
, Index searchIndex
) {
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
);
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);
4531 deletedRowUpsertCount
++;
4534 if (0 == deletedRowUpsertCount
) {
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
;
4550 case LoadOption
.Upsert
:
4551 action
= DataRowAction
.Add
;
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
);
4565 case LoadOption
.Upsert
:
4568 throw ExceptionBuilder
.ArgumentOutOfRange("LoadOption");
4570 RaiseRowChanged(drcevent
, dataRow
, action
);
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
]))
4585 case LoadOption
.Upsert
:
4586 if (dataRow
[dc
, DataRowVersion
.Current
] != dc
[recordNo
])
4589 case LoadOption
.PreserveChanges
:
4590 if (dataRow
[dc
, DataRowVersion
.Original
] != dc
[recordNo
])
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
;
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
;
4619 case DataRowState
.Deleted
:
4620 Debug
.Assert(false, "LoadOption.Upsert with deleted row, should not be here");
4623 action
= DataRowAction
.Change
;
4627 case LoadOption
.PreserveChanges
:
4628 switch(dataRow
.RowState
) {
4629 case DataRowState
.Unchanged
:
4630 action
= DataRowAction
.ChangeCurrentAndOriginal
;
4633 action
= DataRowAction
.ChangeOriginal
;
4638 throw ExceptionBuilder
.ArgumentOutOfRange("LoadOption");
4642 drcevent
= RaiseRowChanging(null, dataRow
, action
);
4643 if (action
== DataRowAction
.Nothing
) { // RaiseRowChanging does not fire for DataRowAction.Nothing
4644 dataRow
.inChangingEvent
= true;
4646 drcevent
= OnRowChanging(drcevent
, dataRow
, action
);
4649 dataRow
.inChangingEvent
= false;
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
);
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
;
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
);
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
);
4694 if (dataRow
.RowState
== DataRowState
.Deleted
)
4695 dataRow
.RejectChanges();
4696 this.SetNewRecord(dataRow
, recordNo
, DataRowAction
.Change
, false, false);
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
);
4711 throw ExceptionBuilder
.ArgumentOutOfRange("LoadOption");
4715 string error
= Res
.GetString(Res
.Load_ReadOnlyDataModified
);
4716 if (dataRow
.RowError
.Length
== 0) { // WebData 112272, append the row error
4717 dataRow
.RowError
= error
;
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;
4733 OnRowChanged(drcevent
, dataRow
, action
);
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
)
4828 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.WriteXml|API> %d#, mode=%d{ds.XmlWriteMode}\n", ObjectID
, (int)mode
);
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);
4841 // Create and save xml data
4842 if (mode
== XmlWriteMode
.WriteSchema
) {
4844 string tablenamespace
= this.tableNamespace
;
4845 if (null == this.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);
4866 ds
.Tables
.Remove(this);
4867 this.tableNamespace
= tablenamespace
;
4871 XmlDataTreeWriter xmldataWriter
= new XmlDataTreeWriter(this, writeHierarchy
);
4872 xmldataWriter
.Save(writer
,/*mode == XmlWriteMode.WriteSchema*/ false);
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
)
4894 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.WriteXml|API> %d#, fileName='%ls', mode=%d{ds.XmlWriteMode}\n", ObjectID
, fileName
, (int)mode
);
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();
4906 Bid
.ScopeLeave(ref hscp
);
4910 public void WriteXmlSchema(Stream stream
) {
4911 WriteXmlSchema(stream
, false);
4914 public void WriteXmlSchema(Stream stream
, bool writeHierarchy
)
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
)
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
>();
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
))) {
4969 public void WriteXmlSchema(XmlWriter writer
) {
4970 WriteXmlSchema(writer
, false);
4973 public void WriteXmlSchema(XmlWriter writer
, bool writeHierarchy
)
4976 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.WriteXmlSchema|API> %d#\n", ObjectID
);
4978 if (this.tableName
.Length
== 0) {
4979 throw ExceptionBuilder
.CanNotSerializeDataTableWithEmptyName();
4982 if (!CheckForClosureOnExpressions(this, writeHierarchy
)) {
4983 throw ExceptionBuilder
.CanNotSerializeDataTableHierarchy();
4987 string tablenamespace
= this.tableNamespace
;//SQL BU Defect Tracking 286968
4989 // Generate SchemaTree and write it out
4990 if (null == this.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
);
5011 ds
.Tables
.Remove(this);
5012 this.tableNamespace
= tablenamespace
;
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 );
5032 xw
.Formatting
= Formatting
.Indented
;
5033 xw
.WriteStartDocument(true);
5034 WriteXmlSchema(xw
, writeHierarchy
);
5035 xw
.WriteEndDocument();
5042 public XmlReadMode
ReadXml(Stream stream
)
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
)
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);
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
;
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
)) {
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
))
5117 internal XmlReadMode
ReadXml(XmlReader reader
, bool denyResolving
)
5120 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.ReadXml|INFO> %d#, denyResolving=%d{bool}\n", ObjectID
, denyResolving
);
5123 DataTable
.RowDiffIdUsageSection rowDiffIdUsage
= new DataTable
.RowDiffIdUsageSection();
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);
5137 bool originalEnforceConstraint
= false;
5138 if (this.DataSet
!= null) {
5139 originalEnforceConstraint
= this.DataSet
.EnforceConstraints
;
5140 this.DataSet
.EnforceConstraints
= false;
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
)) {
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
) {
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
);
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
));
5219 XmlAttribute attr
= topNode
.SetAttributeNode(reader
.LocalName
, reader
.NamespaceURI
);
5220 attr
.Prefix
= reader
.Prefix
;
5221 attr
.Value
= reader
.GetAttribute(i
);
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;
5248 if (reader
.LocalName
== Keywords
.XSD_SCHEMA
&& reader
.NamespaceURI
==Keywords
.XSDNS
) {
5249 // load XSD schema and exit
5250 ReadXmlSchema(reader
, denyResolving
);
5251 fSchemaFound
= true;
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
);
5260 this.enforceConstraints
= originalEnforceConstraint
;
5262 throw ExceptionBuilder
.DataSetUnsupportedSchema(Keywords
.XSDNS
);
5265 if ((reader
.LocalName
== Keywords
.DIFFGRAM
) && (reader
.NamespaceURI
== Keywords
.DFFNS
)) {
5266 this.ReadXmlDiffgram(reader
);
5268 ret
= XmlReadMode
.DiffGram
;
5271 // we found data here
5274 if (!fSchemaFound
&& Columns
.Count
== 0) {
5275 XmlNode node
= xdoc
.ReadNode(reader
);
5276 topNode
.AppendChild(node
);
5279 if (xmlload
== null)
5280 xmlload
= new XmlDataLoader(this, fIsXdr
, topNode
, false);
5281 xmlload
.LoadData(reader
);
5283 ret
= XmlReadMode
.ReadSchema
;
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
)) {
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
);
5315 rowDiffIdUsage
.Cleanup();
5319 Bid
.ScopeLeave(ref hscp
);
5323 internal XmlReadMode
ReadXml(XmlReader reader
, XmlReadMode mode
, bool denyResolving
)
5325 DataTable
.RowDiffIdUsageSection rowDiffIdUsage
= new DataTable
.RowDiffIdUsageSection();
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);
5339 bool originalEnforceConstraint
= false;
5340 if (this.DataSet
!= null) {
5341 originalEnforceConstraint
= this.DataSet
.EnforceConstraints
;
5342 this.DataSet
.EnforceConstraints
= false;
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
)) {
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
) {
5379 return XmlReadMode
.DiffGram
;
5381 throw ExceptionBuilder
.DataTableInferenceNotSupported();
5383 this.ReadXmlDiffgram(reader
);
5384 // read the closing tag of the current element
5385 ReadEndElement(reader
);
5390 RestoreConstraint(originalEnforceConstraint
);
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
);
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
);
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
);
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
));
5437 XmlAttribute attr
= topNode
.SetAttributeNode(reader
.LocalName
, reader
.NamespaceURI
);
5438 attr
.Prefix
= reader
.Prefix
;
5439 attr
.Value
= reader
.GetAttribute(i
);
5446 while(MoveToElement(reader
, iCurrentDepth
)) {
5448 if (reader
.LocalName
== Keywords
.XDR_SCHEMA
&& reader
.NamespaceURI
==Keywords
.XDRNS
) {
5450 if (!fSchemaFound
&& !fDataFound
&& (mode
!= XmlReadMode
.IgnoreSchema
) && (mode
!= XmlReadMode
.InferSchema
)) {
5451 ReadXDRSchema(reader
);
5452 fSchemaFound
= true;
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;
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
) {
5478 return XmlReadMode
.DiffGram
;
5480 throw ExceptionBuilder
.DataTableInferenceNotSupported();
5482 this.ReadXmlDiffgram(reader
);
5483 ret
= XmlReadMode
.DiffGram
;
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
);
5496 this.enforceConstraints
= originalEnforceConstraint
;
5498 throw ExceptionBuilder
.DataSetUnsupportedSchema(Keywords
.XSDNS
);
5501 if (mode
== XmlReadMode
.DiffGram
) {
5503 continue; // we do not read data in diffgram mode
5506 // if we are here we found some data
5509 if (mode
== XmlReadMode
.InferSchema
) { //save the node in DOM until the end;
5510 XmlNode node
= xdoc
.ReadNode(reader
);
5511 topNode
.AppendChild(node
);
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
);
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
);
5553 // Dev11 904428: prepare and cleanup rowDiffId hashtable
5554 rowDiffIdUsage
.Cleanup();
5559 internal void ReadEndElement(XmlReader reader
) {
5560 while (reader
.NodeType
== XmlNodeType
.Whitespace
) {
5563 if (reader
.NodeType
== XmlNodeType
.None
) {
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
) {
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;
5589 if (this.Rows
.Count
== 0) {
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
))
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
);
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
) {
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
);
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);
5677 ReadEndElement(reader
);
5681 XSDSchema schema
= new XSDSchema();
5682 schema
.LoadSchema(sSet
, this);
5686 public void ReadXmlSchema(Stream stream
)
5691 ReadXmlSchema( new XmlTextReader( stream
), false );
5694 public void ReadXmlSchema(TextReader reader
)
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
);
5708 ReadXmlSchema( xr
, false );
5715 public void ReadXmlSchema(XmlReader reader
)
5717 ReadXmlSchema(reader
, false);
5720 internal void ReadXmlSchema(XmlReader reader
, bool denyResolving
)
5723 Bid
.ScopeEnter(out hscp
, "<ds.DataTable.ReadXmlSchema|INFO> %d#, denyResolving=%d{bool}\n", ObjectID
, denyResolving
);
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
))
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
;
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
;
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
);
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
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;
5853 if (!hasExternaldependency
) {
5854 this.DataSet
.Tables
[tempTable
.TableName
, tempTable
.Namespace
].Columns
[dc
.ColumnName
].Expression
= dc
.Expression
;
5857 hasExternaldependency
= false;
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 -->
5891 <s:element name="anyUserSpecifiedMethodName"+"Response">
5894 <s:element minOccurs="0" maxOccurs="1" name="anyUserSpecifiedMethodName"+"Result">
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" />
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
;
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
;
5937 XmlSchema IXmlSerializable
.GetSchema() {
5941 protected virtual XmlSchema
GetSchema() {
5942 if (GetType() == typeof(DataTable
)) {
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 {
5986 return this.serializeHierarchy;
5989 this.serializeHierarchy = value;
5994 // RowDiffIdUsageSection & DSRowDiffIdUsageSection Usage:
5996 // DataTable.[DS]RowDiffIdUsageSection rowDiffIdUsage = new DataTable.[DS]RowDiffIdUsageSection();
5998 // rowDiffIdUsage.Prepare(DataTable or DataSet, depending on type);
6000 // // code that requires RowDiffId usage
6004 // rowDiffIdUsage.Cleanup();
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
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.
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!
6022 internal static List
<DataTable
> s_usedTables
;
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");
6033 Debug
.Assert(s_usedTables
== null || !s_usedTables
.Contains(table
),
6034 "Nested call with same table can cause data corruption!");
6038 if (s_usedTables
== null)
6039 s_usedTables
= new List
<DataTable
>();
6040 s_usedTables
.Add(table
);
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)
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;
6061 _targetTable
.rowDiffId
= null;
6065 [Conditional("DEBUG")]
6066 internal static void Assert(string message
)
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
);
6076 internal struct DSRowDiffIdUsageSection
6080 internal void Prepare(DataSet ds
)
6082 Debug
.Assert(_targetDS
== null, "do not reuse this section");
6083 Debug
.Assert(ds
!= null);
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
>();
6092 for (int tableIndex
= 0; tableIndex
< ds
.Tables
.Count
; ++tableIndex
)
6094 DataTable table
= ds
.Tables
[tableIndex
];
6096 Debug
.Assert(!RowDiffIdUsageSection
.s_usedTables
.Contains(table
), "Nested call with same table can cause data corruption!");
6097 RowDiffIdUsageSection
.s_usedTables
.Add(table
);
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)
6111 Debug
.Assert(RowDiffIdUsageSection
.s_usedTables
!= null, "missing Prepare before Cleanup");
6114 for (int tableIndex
= 0; tableIndex
< _targetDS
.Tables
.Count
; ++tableIndex
)
6116 DataTable table
= _targetDS
.Tables
[tableIndex
];
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
);
6123 table
.rowDiffId
= null;
6126 if (RowDiffIdUsageSection
.s_usedTables
!= null && RowDiffIdUsageSection
.s_usedTables
.Count
== 0)
6127 RowDiffIdUsageSection
.s_usedTables
= null; // out-of-scope
6133 internal Hashtable RowDiffId
{
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();
6144 internal int 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
);
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);
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
;
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)
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
) {
6330 else if (expressionVersion
== DataRowVersion
.Original
&& (dr
.oldRecord
== -1 || dr
.oldRecord
== dr
.newRecord
)) {
6335 newValue
= dc
.DataExpression
.Evaluate(dr
, expressionVersion
);
6337 SilentlySetValue(dr
, dc
, expressionVersion
, newValue
);
6341 if (row
.RowState
== DataRowState
.Deleted
) {
6344 else if (version
== DataRowVersion
.Original
&& (row
.oldRecord
== -1 || row
.oldRecord
== row
.newRecord
)) {
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
)
6364 // don't update original version if child row doesn't have an oldRecord.
6365 if (foreignVer
== DataRowVersion
.Original
&& cachedRow
.newRecord
== cachedRow
.oldRecord
)
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
)
6379 foreach (DataRow parentRow
in row
.GetParentRows(relation
, version
)) {
6380 if (cachedRows
!= null && cachedRows
.Contains(parentRow
))
6382 // don't update original version if child row doesn't have an oldRecord.
6383 if (foreignVer
== DataRowVersion
.Original
&& parentRow
.newRecord
== parentRow
.oldRecord
)
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
)
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
))
6400 if (foreignVer
== DataRowVersion
.Original
&& childRow
.newRecord
== childRow
.oldRecord
)
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
);