2 // System.Data/DataSet.cs
5 // Christopher Podurgiel <cpodurgiel@msn.com>
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Rodrigo Moya <rodrigo@ximian.com>
8 // Stuart Caborn <stuart.caborn@virgin.net>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
11 // Atsushi Enomoto <atsushi@ximian.com>
13 // (C) Ximian, Inc. 2002
14 // Copyright (C) Tim Coleman, 2002, 2003
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 using System.Collections;
42 using System.ComponentModel;
43 using System.Globalization;
44 using System.Threading;
46 using System.Runtime.Serialization;
48 using System.Xml.Schema;
49 using System.Xml.Serialization;
50 using System.Data.Common;
52 namespace System.Data {
55 [DefaultProperty ("DataSetName")]
56 [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataSetDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
59 public class DataSet : MarshalByValueComponent, IListSource,
60 ISupportInitialize, ISerializable, IXmlSerializable
62 private string dataSetName;
63 private string _namespace = "";
64 private string prefix;
65 private bool caseSensitive;
66 private bool enforceConstraints = true;
67 private DataTableCollection tableCollection;
68 private DataRelationCollection relationCollection;
69 private PropertyCollection properties;
70 private DataViewManager defaultView;
71 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
72 internal XmlDataDocument _xmlDataDocument = null;
76 public DataSet () : this ("NewDataSet")
80 public DataSet (string name)
83 tableCollection = new DataTableCollection (this);
84 relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
85 properties = new PropertyCollection ();
86 this.prefix = String.Empty;
88 this.Locale = CultureInfo.CurrentCulture;
91 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
93 GetSerializationData (info, context);
96 #endregion // Constructors
98 #region Public Properties
100 [DataCategory ("Data")]
101 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
102 [DefaultValue (false)]
103 public bool CaseSensitive {
105 return caseSensitive;
108 caseSensitive = value;
109 if (!caseSensitive) {
110 foreach (DataTable table in Tables) {
111 foreach (Constraint c in table.Constraints)
112 c.AssertConstraint ();
118 [DataCategory ("Data")]
119 [DataSysDescription ("The name of this DataSet.")]
121 public string DataSetName {
122 get { return dataSetName; }
123 set { dataSetName = value; }
126 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
128 public DataViewManager DefaultViewManager {
130 if (defaultView == null)
131 defaultView = new DataViewManager (this);
136 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
137 [DefaultValue (true)]
138 public bool EnforceConstraints {
139 get { return enforceConstraints; }
141 if (value != enforceConstraints) {
142 enforceConstraints = value;
144 foreach (DataTable table in Tables) {
145 // first assert all unique constraints
146 foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
147 uc.AssertConstraint ();
148 // then assert all foreign keys
149 foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
150 fk.AssertConstraint ();
158 [DataCategory ("Data")]
159 [DataSysDescription ("The collection that holds custom user information.")]
160 public PropertyCollection ExtendedProperties {
161 get { return properties; }
165 [DataSysDescription ("Indicates that the DataSet has errors.")]
166 public bool HasErrors {
169 for (int i = 0; i < Tables.Count; i++) {
170 if (Tables[i].HasErrors)
177 [DataCategory ("Data")]
178 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
179 public CultureInfo Locale {
184 if (locale == null || !locale.Equals (value)) {
185 // TODO: check if the new locale is valid
186 // TODO: update locale of all tables
192 public void Merge (DataRow[] rows)
194 Merge (rows, false, MissingSchemaAction.Add);
197 public void Merge (DataSet dataSet)
199 Merge (dataSet, false, MissingSchemaAction.Add);
202 public void Merge (DataTable table)
204 Merge (table, false, MissingSchemaAction.Add);
207 public void Merge (DataSet dataSet, bool preserveChanges)
209 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
213 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
216 throw new ArgumentNullException ("rows");
217 if (!IsLegalSchemaAction (missingSchemaAction))
218 throw new ArgumentOutOfRangeException ("missingSchemaAction");
220 MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
224 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
227 throw new ArgumentNullException ("dataSet");
228 if (!IsLegalSchemaAction (missingSchemaAction))
229 throw new ArgumentOutOfRangeException ("missingSchemaAction");
231 MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
235 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
238 throw new ArgumentNullException ("table");
239 if (!IsLegalSchemaAction (missingSchemaAction))
240 throw new ArgumentOutOfRangeException ("missingSchemaAction");
242 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
245 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
247 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
248 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
253 [DataCategory ("Data")]
254 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
256 public string Namespace {
257 get { return _namespace; }
259 //TODO - trigger an event if this happens?
261 value = String.Empty;
262 if (value != this._namespace)
263 RaisePropertyChanging ("Namespace");
268 [DataCategory ("Data")]
269 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
271 public string Prefix {
272 get { return prefix; }
275 value = String.Empty;
276 // Prefix cannot contain any special characters other than '_' and ':'
277 for (int i = 0; i < value.Length; i++) {
278 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
279 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
284 value = string.Empty;
286 if (value != this.prefix)
287 RaisePropertyChanging ("Prefix");
292 [DataCategory ("Data")]
293 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
294 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
295 public DataRelationCollection Relations {
297 return relationCollection;
302 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
303 public override ISite Site {
306 throw new NotImplementedException ();
311 throw new NotImplementedException ();
315 [DataCategory ("Data")]
316 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
318 public DataTableCollection Tables {
319 get { return tableCollection; }
322 #endregion // Public Properties
324 #region Public Methods
327 public void AcceptChanges ()
329 foreach (DataTable tempTable in tableCollection)
330 tempTable.AcceptChanges ();
334 /// Clears all the tables
338 if (_xmlDataDocument != null)
339 throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
340 bool enforceConstraints = this.EnforceConstraints;
341 this.EnforceConstraints = false;
342 for (int t = 0; t < tableCollection.Count; t++) {
343 tableCollection[t].Clear ();
345 this.EnforceConstraints = enforceConstraints;
348 public virtual DataSet Clone ()
350 // need to return the same type as this...
351 DataSet Copy = (DataSet) Activator.CreateInstance(GetType(), true);
353 CopyProperties (Copy);
355 foreach (DataTable Table in Tables) {
356 // tables are often added in no-args constructor, don't add them
358 if (!Copy.Tables.Contains(Table.TableName)) {
359 Copy.Tables.Add (Table.Clone ());
363 //Copy Relationships between tables after existance of tables
364 //and setting properties correctly
365 CopyRelations (Copy);
370 // Copies both the structure and data for this DataSet.
371 public DataSet Copy ()
373 DataSet Copy = new DataSet ();
374 CopyProperties (Copy);
376 // Copy DatSet's tables
377 foreach (DataTable Table in Tables)
378 Copy.Tables.Add (Table.Copy ());
380 //Copy Relationships between tables after existance of tables
381 //and setting properties correctly
382 CopyRelations (Copy);
387 private void CopyProperties (DataSet Copy)
389 Copy.CaseSensitive = CaseSensitive;
390 //Copy.Container = Container
391 Copy.DataSetName = DataSetName;
392 //Copy.DefaultViewManager
394 Copy.EnforceConstraints = EnforceConstraints;
395 if(ExtendedProperties.Count > 0) {
396 // Cannot copy extended properties directly as the property does not have a set accessor
397 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
398 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
399 for (int i=0; i < ExtendedProperties.Count; i++)
400 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
402 Copy.Locale = Locale;
403 Copy.Namespace = Namespace;
404 Copy.Prefix = Prefix;
405 //Copy.Site = Site; // FIXME : Not sure of this.
410 private void CopyRelations (DataSet Copy)
413 //Creation of the relation contains some of the properties, and the constructor
414 //demands these values. instead changing the DataRelation constructor and behaviour the
415 //parameters are pre-configured and sent to the most general constructor
417 foreach (DataRelation MyRelation in this.Relations) {
418 string pTable = MyRelation.ParentTable.TableName;
419 string cTable = MyRelation.ChildTable.TableName;
420 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
421 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
424 foreach (DataColumn DC in MyRelation.ParentColumns) {
425 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
431 foreach (DataColumn DC in MyRelation.ChildColumns) {
432 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
436 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
437 //cRel.ChildColumns = MyRelation.ChildColumns;
438 //cRel.ChildTable = MyRelation.ChildTable;
439 //cRel.ExtendedProperties = cRel.ExtendedProperties;
440 //cRel.Nested = MyRelation.Nested;
441 //cRel.ParentColumns = MyRelation.ParentColumns;
442 //cRel.ParentTable = MyRelation.ParentTable;
444 Copy.Relations.Add (cRel);
451 public DataSet GetChanges ()
453 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
457 public DataSet GetChanges (DataRowState rowStates)
459 if (!HasChanges (rowStates))
462 DataSet copySet = Clone ();
463 Hashtable addedRows = new Hashtable ();
465 IEnumerator tableEnumerator = Tables.GetEnumerator ();
468 while (tableEnumerator.MoveNext ()) {
469 origTable = (DataTable)tableEnumerator.Current;
470 copyTable = copySet.Tables[origTable.TableName];
472 // Look for relations that have this table as child
473 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
475 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
476 while (rowEnumerator.MoveNext ()) {
477 DataRow row = (DataRow)rowEnumerator.Current;
479 if (row.IsRowChanged (rowStates))
480 AddChangedRow (addedRows, copySet, copyTable, relations, row);
486 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
488 if (addedRows.ContainsKey (row)) return;
491 while (relations.MoveNext ()) {
492 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
493 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
494 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
495 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
498 DataRow newRow = copyTable.NewRow ();
499 row.CopyValuesToRow (newRow);
500 copyTable.Rows.Add (newRow);
501 newRow.XmlRowID = row.XmlRowID;
502 addedRows.Add (row,row);
507 public DataTableReader GetDataReader (DataTable[] dataTables)
509 throw new NotImplementedException ();
513 public DataTableReader GetDataReader ()
515 throw new NotImplementedException ();
519 public string GetXml ()
521 StringWriter Writer = new StringWriter ();
522 WriteXml (Writer, XmlWriteMode.IgnoreSchema);
523 return Writer.ToString ();
526 public string GetXmlSchema ()
528 StringWriter Writer = new StringWriter ();
529 WriteXmlSchema (Writer);
530 return Writer.ToString ();
534 public bool HasChanges ()
536 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
540 public bool HasChanges (DataRowState rowState)
542 if (((int)rowState & 0xffffffe0) != 0)
543 throw new ArgumentOutOfRangeException ("rowState");
545 DataTableCollection tableCollection = Tables;
547 DataRowCollection rowCollection;
550 for (int i = 0; i < tableCollection.Count; i++) {
551 table = tableCollection[i];
552 rowCollection = table.Rows;
553 for (int j = 0; j < rowCollection.Count; j++) {
554 row = rowCollection[j];
555 if ((row.RowState & rowState) != 0)
563 public void InferXmlSchema (XmlReader reader, string[] nsArray)
567 XmlDocument doc = new XmlDocument ();
569 InferXmlSchema (doc, nsArray);
572 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
574 XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
577 public void InferXmlSchema (Stream stream, string[] nsArray)
579 InferXmlSchema (new XmlTextReader (stream), nsArray);
582 public void InferXmlSchema (TextReader reader, string[] nsArray)
584 InferXmlSchema (new XmlTextReader (reader), nsArray);
587 public void InferXmlSchema (string fileName, string[] nsArray)
589 XmlTextReader reader = new XmlTextReader (fileName);
591 InferXmlSchema (reader, nsArray);
599 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
601 throw new NotImplementedException ();
605 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
607 throw new NotImplementedException ();
611 public virtual void RejectChanges ()
614 bool oldEnforceConstraints = this.EnforceConstraints;
615 this.EnforceConstraints = false;
617 for (i = 0; i < this.Tables.Count;i++)
618 this.Tables[i].RejectChanges ();
620 this.EnforceConstraints = oldEnforceConstraints;
623 public virtual void Reset ()
625 IEnumerator constraintEnumerator;
627 // first we remove all ForeignKeyConstraints (if we will not do that
628 // we will get an exception when clearing the tables).
629 for (int i = 0; i < Tables.Count; i++) {
630 ConstraintCollection cc = Tables[i].Constraints;
631 for (int j = 0; j < cc.Count; j++) {
632 if (cc[j] is ForeignKeyConstraint)
642 public void WriteXml (Stream stream)
644 XmlTextWriter writer = new XmlTextWriter (stream, null);
645 writer.Formatting = Formatting.Indented;
650 /// Writes the current data for the DataSet to the specified file.
652 /// <param name="filename">Fully qualified filename to write to</param>
653 public void WriteXml (string fileName)
655 XmlTextWriter writer = new XmlTextWriter (fileName, null);
656 writer.Formatting = Formatting.Indented;
657 writer.WriteStartDocument (true);
662 writer.WriteEndDocument ();
667 public void WriteXml (TextWriter writer)
669 XmlTextWriter xwriter = new XmlTextWriter (writer);
670 xwriter.Formatting = Formatting.Indented;
674 public void WriteXml (XmlWriter writer)
676 WriteXml (writer, XmlWriteMode.IgnoreSchema);
679 public void WriteXml (string filename, XmlWriteMode mode)
681 XmlTextWriter writer = new XmlTextWriter (filename, null);
682 writer.Formatting = Formatting.Indented;
683 writer.WriteStartDocument (true);
686 WriteXml (writer, mode);
689 writer.WriteEndDocument ();
694 public void WriteXml (Stream stream, XmlWriteMode mode)
696 XmlTextWriter writer = new XmlTextWriter (stream, null);
697 writer.Formatting = Formatting.Indented;
698 WriteXml (writer, mode);
701 public void WriteXml (TextWriter writer, XmlWriteMode mode)
703 XmlTextWriter xwriter = new XmlTextWriter (writer);
704 xwriter.Formatting = Formatting.Indented;
705 WriteXml (xwriter, mode);
708 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
710 if (mode == XmlWriteMode.DiffGram) {
712 WriteDiffGramElement(writer);
715 // It should not write when there is no content to be written
716 bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
717 for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
718 shouldOutputContent = tableCollection [n].Rows.Count > 0;
720 if (shouldOutputContent) {
721 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
723 if (mode == XmlWriteMode.WriteSchema)
724 DoWriteXmlSchema (writer);
726 WriteTables (writer, mode, Tables, DataRowVersion.Default);
727 writer.WriteEndElement ();
730 if (mode == XmlWriteMode.DiffGram) {
731 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
733 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
734 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
735 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
736 writer.WriteEndElement ();
740 if (mode == XmlWriteMode.DiffGram)
741 writer.WriteEndElement (); // diffgr:diffgram
746 public void WriteXmlSchema (Stream stream)
748 XmlTextWriter writer = new XmlTextWriter (stream, null );
749 writer.Formatting = Formatting.Indented;
750 WriteXmlSchema (writer);
753 public void WriteXmlSchema (string fileName)
755 XmlTextWriter writer = new XmlTextWriter (fileName, null);
757 writer.Formatting = Formatting.Indented;
758 writer.WriteStartDocument (true);
759 WriteXmlSchema (writer);
761 writer.WriteEndDocument ();
766 public void WriteXmlSchema (TextWriter writer)
768 XmlTextWriter xwriter = new XmlTextWriter (writer);
770 xwriter.Formatting = Formatting.Indented;
771 WriteXmlSchema (xwriter);
777 public void WriteXmlSchema (XmlWriter writer)
779 //Create a skeleton doc and then write the schema
780 //proper which is common to the WriteXml method in schema mode
781 DoWriteXmlSchema (writer);
784 public void ReadXmlSchema (Stream stream)
786 XmlReader reader = new XmlTextReader (stream, null);
787 ReadXmlSchema (reader);
790 public void ReadXmlSchema (string str)
792 XmlReader reader = new XmlTextReader (str);
794 ReadXmlSchema (reader);
801 public void ReadXmlSchema (TextReader treader)
803 XmlReader reader = new XmlTextReader (treader);
804 ReadXmlSchema (reader);
807 public void ReadXmlSchema (XmlReader reader)
810 new XmlSchemaDataImporter (this, reader).Process ();
812 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
813 SchemaMapper.Read (reader);
817 public XmlReadMode ReadXml (Stream stream)
819 return ReadXml (new XmlTextReader (stream));
822 public XmlReadMode ReadXml (string str)
824 XmlTextReader reader = new XmlTextReader (str);
826 return ReadXml (reader);
833 public XmlReadMode ReadXml (TextReader reader)
835 return ReadXml (new XmlTextReader (reader));
838 public XmlReadMode ReadXml (XmlReader r)
840 return ReadXml (r, XmlReadMode.Auto);
843 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
845 return ReadXml (new XmlTextReader (stream), mode);
848 public XmlReadMode ReadXml (string str, XmlReadMode mode)
850 XmlTextReader reader = new XmlTextReader (str);
852 return ReadXml (reader, mode);
859 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
861 return ReadXml (new XmlTextReader (reader), mode);
864 // LAMESPEC: XmlReadMode.Fragment is far from presisely
865 // documented. MS.NET infers schema against this mode.
866 public XmlReadMode ReadXml (XmlReader input, XmlReadMode mode)
868 switch (input.ReadState) {
869 case ReadState.EndOfFile:
870 case ReadState.Error:
871 case ReadState.Closed:
874 // Skip XML declaration and prolog
875 input.MoveToContent ();
879 // FIXME: We need more decent code here, but for now
880 // I don't know the precise MS.NET behavior, I just
881 // delegate to specific read process.
883 case XmlReadMode.IgnoreSchema:
884 return ReadXmlIgnoreSchema (input, mode, true);
885 case XmlReadMode.ReadSchema:
886 return ReadXmlReadSchema (input, mode, true);
888 // remaining modes are: Auto, InferSchema, Fragment, Diffgram
890 XmlReader reader = input;
892 int depth = reader.Depth;
893 XmlReadMode result = mode;
894 bool skippedTopLevelElement = false;
895 string potentialDataSetName = null;
896 XmlDocument doc = null;
897 bool shouldReadData = mode != XmlReadMode.DiffGram;
898 bool shouldNotInfer = Tables.Count > 0;
901 case XmlReadMode.Auto:
902 case XmlReadMode.InferSchema:
903 doc = new XmlDocument ();
905 doc.AppendChild (doc.ReadNode (reader));
906 } while (!reader.EOF &&
907 doc.DocumentElement == null);
908 reader = new XmlNodeReader (doc);
909 reader.MoveToContent ();
911 case XmlReadMode.DiffGram:
912 if (!(reader.LocalName == "diffgram" &&
913 reader.NamespaceURI == XmlConstants.DiffgrNamespace))
914 goto case XmlReadMode.Auto;
919 case XmlReadMode.Auto:
920 case XmlReadMode.InferSchema:
921 case XmlReadMode.ReadSchema:
922 if (!(reader.LocalName == "diffgram" &&
923 reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
924 !(reader.LocalName == "schema" &&
925 reader.NamespaceURI == XmlSchema.Namespace))
926 potentialDataSetName = reader.LocalName;
928 case XmlReadMode.Fragment:
931 if (!(reader.LocalName == "diffgram" &&
932 reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
933 !(reader.LocalName == "schema" &&
934 reader.NamespaceURI == XmlSchema.Namespace)) {
935 if (!reader.IsEmptyElement) {
937 reader.MoveToContent ();
938 skippedTopLevelElement = true;
942 case XmlReadMode.Auto:
943 case XmlReadMode.InferSchema:
944 DataSetName = reader.LocalName;
953 // If schema, then read the first element as schema
954 if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
955 shouldNotInfer = true;
957 case XmlReadMode.IgnoreSchema:
958 case XmlReadMode.InferSchema:
961 case XmlReadMode.Fragment:
962 ReadXmlSchema (reader);
964 case XmlReadMode.DiffGram:
965 case XmlReadMode.Auto:
966 if (Tables.Count == 0) {
967 ReadXmlSchema (reader);
968 if (mode == XmlReadMode.Auto)
969 result = XmlReadMode.ReadSchema;
971 // otherwise just ignore and return IgnoreSchema
973 result = XmlReadMode.IgnoreSchema;
976 case XmlReadMode.ReadSchema:
977 ReadXmlSchema (reader);
982 // If diffgram, then read the first element as diffgram
983 if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
985 case XmlReadMode.Auto:
986 case XmlReadMode.IgnoreSchema:
987 case XmlReadMode.DiffGram:
988 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
989 DiffLoader.Load (reader);
990 if (mode == XmlReadMode.Auto)
991 result = XmlReadMode.DiffGram;
992 shouldReadData = false;
994 case XmlReadMode.Fragment:
1003 // if schema after diffgram, just skip it.
1004 if (!shouldReadData && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
1005 shouldNotInfer = true;
1010 case XmlReadMode.ReadSchema:
1011 case XmlReadMode.DiffGram:
1012 if (Tables.Count == 0)
1013 ReadXmlSchema (reader);
1019 return result == XmlReadMode.Auto ?
1020 potentialDataSetName != null && !shouldNotInfer ?
1021 XmlReadMode.InferSchema :
1022 XmlReadMode.IgnoreSchema : result;
1024 // Otherwise, read as dataset... but only when required.
1025 if (shouldReadData && !shouldNotInfer) {
1027 case XmlReadMode.Auto:
1028 if (Tables.Count > 0)
1029 goto case XmlReadMode.IgnoreSchema;
1031 goto case XmlReadMode.InferSchema;
1032 case XmlReadMode.InferSchema:
1033 InferXmlSchema (doc, null);
1034 if (mode == XmlReadMode.Auto)
1035 result = XmlReadMode.InferSchema;
1037 case XmlReadMode.IgnoreSchema:
1038 case XmlReadMode.Fragment:
1039 case XmlReadMode.DiffGram:
1042 shouldReadData = false;
1047 if (shouldReadData) {
1048 XmlReader dataReader = reader;
1050 dataReader = new XmlNodeReader (doc);
1051 dataReader.MoveToContent ();
1053 if (reader.NodeType == XmlNodeType.Element)
1054 XmlDataReader.ReadXml (this, dataReader,
1058 if (skippedTopLevelElement) {
1060 case XmlReadMode.Auto:
1061 case XmlReadMode.InferSchema:
1062 // DataSetName = potentialDataSetName;
1063 // result = XmlReadMode.InferSchema;
1066 if (reader.NodeType == XmlNodeType.EndElement)
1067 reader.ReadEndElement ();
1070 while (input.Depth > depth)
1072 if (input.NodeType == XmlNodeType.EndElement)
1075 input.MoveToContent ();
1077 return result == XmlReadMode.Auto ?
1078 XmlReadMode.IgnoreSchema : result;
1080 #endregion // Public Methods
1082 #region Public Events
1084 [DataCategory ("Action")]
1085 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
1086 public event MergeFailedEventHandler MergeFailed;
1088 #endregion // Public Events
1090 #region IListSource methods
1091 IList IListSource.GetList ()
1093 return DefaultViewManager;
1096 bool IListSource.ContainsListCollection {
1101 #endregion IListSource methods
1103 #region ISupportInitialize methods
1104 public void BeginInit ()
1108 public void EndInit ()
1113 #region ISerializable
1114 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1116 StringWriter sw = new StringWriter ();
1117 XmlTextWriter writer = new XmlTextWriter (sw);
1118 DoWriteXmlSchema (writer);
1120 si.AddValue ("XmlSchema", sw.ToString ());
1122 sw = new StringWriter ();
1123 writer = new XmlTextWriter (sw);
1124 WriteXml (writer, XmlWriteMode.DiffGram);
1126 si.AddValue ("XmlDiffGram", sw.ToString ());
1130 #region Protected Methods
1131 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1133 string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1134 XmlTextReader reader = new XmlTextReader (new StringReader (s));
1135 ReadXmlSchema (reader);
1138 s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1139 reader = new XmlTextReader (new StringReader (s));
1140 ReadXml (reader, XmlReadMode.DiffGram);
1145 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1150 protected virtual void ReadXmlSerializable (XmlReader reader)
1155 void IXmlSerializable.ReadXml (XmlReader reader)
1157 ReadXmlSerializable(reader);
1160 void IXmlSerializable.WriteXml (XmlWriter writer)
1162 DoWriteXmlSchema (writer);
1163 WriteXml (writer, XmlWriteMode.DiffGram);
1166 XmlSchema IXmlSerializable.GetSchema ()
1168 return GetSchemaSerializable ();
1171 protected virtual bool ShouldSerializeRelations ()
1176 protected virtual bool ShouldSerializeTables ()
1182 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1187 protected virtual void OnRemoveRelation (DataRelation relation)
1192 protected virtual void OnRemoveTable (DataTable table)
1196 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1198 if (MergeFailed != null)
1199 MergeFailed (this, e);
1203 protected internal void RaisePropertyChanging (string name)
1208 #region Private Methods
1210 private XmlReadMode ReadXmlIgnoreSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
1212 if (input.LocalName == "schema" &&
1213 input.NamespaceURI == XmlSchema.Namespace) {
1216 else if (input.LocalName == "diffgram" &&
1217 input.NamespaceURI == XmlConstants.DiffgrNamespace) {
1218 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
1219 DiffLoader.Load (input);
1221 else if (checkRecurse ||
1222 input.LocalName == DataSetName &&
1223 input.NamespaceURI == Namespace) {
1224 XmlDataReader.ReadXml (this, input, mode);
1226 else if (checkRecurse && !input.IsEmptyElement) {
1227 int depth = input.Depth;
1229 input.MoveToContent ();
1230 ReadXmlIgnoreSchema (input, mode, false);
1231 while (input.Depth > depth)
1233 if (input.NodeType == XmlNodeType.EndElement)
1234 input.ReadEndElement ();
1236 input.MoveToContent ();
1237 return XmlReadMode.IgnoreSchema;
1240 private XmlReadMode ReadXmlReadSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
1242 if (input.LocalName == "schema" &&
1243 input.NamespaceURI == XmlSchema.Namespace) {
1244 ReadXmlSchema (input);
1246 else if (checkRecurse && !input.IsEmptyElement) {
1247 int depth = input.Depth;
1249 input.MoveToContent ();
1250 ReadXmlReadSchema (input, mode, false);
1251 while (input.Depth > depth)
1253 if (input.NodeType == XmlNodeType.EndElement)
1254 input.ReadEndElement ();
1258 input.MoveToContent ();
1259 return XmlReadMode.ReadSchema;
1262 internal static string WriteObjectXml (object o)
1264 switch (Type.GetTypeCode (o.GetType ())) {
1265 case TypeCode.Boolean:
1266 return XmlConvert.ToString ((Boolean) o);
1268 return XmlConvert.ToString ((Byte) o);
1270 return XmlConvert.ToString ((Char) o);
1271 case TypeCode.DateTime:
1272 return XmlConvert.ToString ((DateTime) o);
1273 case TypeCode.Decimal:
1274 return XmlConvert.ToString ((Decimal) o);
1275 case TypeCode.Double:
1276 return XmlConvert.ToString ((Double) o);
1277 case TypeCode.Int16:
1278 return XmlConvert.ToString ((Int16) o);
1279 case TypeCode.Int32:
1280 return XmlConvert.ToString ((Int32) o);
1281 case TypeCode.Int64:
1282 return XmlConvert.ToString ((Int64) o);
1283 case TypeCode.SByte:
1284 return XmlConvert.ToString ((SByte) o);
1285 case TypeCode.Single:
1286 return XmlConvert.ToString ((Single) o);
1287 case TypeCode.UInt16:
1288 return XmlConvert.ToString ((UInt16) o);
1289 case TypeCode.UInt32:
1290 return XmlConvert.ToString ((UInt32) o);
1291 case TypeCode.UInt64:
1292 return XmlConvert.ToString ((UInt64) o);
1294 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1295 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1296 if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1297 return o.ToString ();
1300 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1302 //Write out each table in order, providing it is not
1303 //part of another table structure via a nested parent relationship
1304 foreach (DataTable table in tableCollection) {
1305 bool isTopLevel = true;
1307 foreach (DataRelation rel in table.ParentRelations) {
1315 WriteTable ( writer, table, mode, version);
1320 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1322 DataRow[] rows = new DataRow [table.Rows.Count];
1323 table.Rows.CopyTo (rows, 0);
1324 WriteTable (writer, rows, mode, version, true);
1327 private void WriteTable (XmlWriter writer,
1330 DataRowVersion version, bool skipIfNested)
1332 //The columns can be attributes, hidden, elements, or simple content
1333 //There can be 0-1 simple content cols or 0-* elements
1334 System.Collections.ArrayList atts;
1335 System.Collections.ArrayList elements;
1336 DataColumn simple = null;
1338 if (rows.Length == 0) return;
1339 DataTable table = rows[0].Table;
1340 SplitColumns (table, out atts, out elements, out simple);
1341 //sort out the namespacing
1342 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1343 int relationCount = table.ParentRelations.Count;
1344 DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1346 foreach (DataRow row in rows) {
1348 // Skip rows that is a child of any tables.
1349 switch (relationCount) {
1355 if (row.GetParentRow (oneRel) != null)
1360 for (int i = 0; i < table.ParentRelations.Count; i++) {
1361 DataRelation prel = table.ParentRelations [i];
1364 if (row.GetParentRow (prel) != null) {
1375 if (!row.HasVersion(version) ||
1376 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1377 && version == DataRowVersion.Original))
1380 // First check are all the rows null. If they are we just write empty element
1381 bool AllNulls = true;
1382 foreach (DataColumn dc in table.Columns) {
1384 if (row [dc.ColumnName, version] != DBNull.Value) {
1390 // If all of the columns were null, we have to write empty element
1392 writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1396 WriteTableElement (writer, mode, table, row, version);
1398 foreach (DataColumn col in atts) {
1399 WriteColumnAsAttribute (writer, mode, col, row, version);
1402 if (simple != null) {
1403 writer.WriteString (WriteObjectXml (row[simple, version]));
1406 foreach (DataColumn col in elements) {
1407 WriteColumnAsElement (writer, mode, col, row, version);
1411 foreach (DataRelation relation in table.ChildRelations) {
1412 if (relation.Nested) {
1413 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1417 writer.WriteEndElement ();
1422 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1424 string colnspc = null;
1425 object rowObject = row [col, version];
1427 if (rowObject == null || rowObject == DBNull.Value)
1430 if (col.Namespace != String.Empty)
1431 colnspc = col.Namespace;
1433 //TODO check if I can get away with write element string
1434 WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1435 writer.WriteString (WriteObjectXml (rowObject));
1436 writer.WriteEndElement ();
1439 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1441 if (!row.IsNull (col))
1442 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1445 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1447 //sort out the namespacing
1448 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1450 WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1452 if (mode == XmlWriteMode.DiffGram) {
1453 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1454 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1455 string modeName = null;
1456 if (row.RowState == DataRowState.Modified)
1457 modeName = "modified";
1458 else if (row.RowState == DataRowState.Added)
1459 modeName = "inserted";
1461 if (version != DataRowVersion.Original && modeName != null)
1462 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1466 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1468 writer.WriteStartElement (prefix, name, nspc);
1471 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1474 case XmlWriteMode.WriteSchema:
1475 writer.WriteAttributeString (prefix, name, nspc);
1477 case XmlWriteMode.DiffGram:
1478 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1481 writer.WriteAttributeString (name, stringValue);
1486 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1488 if (mode == XmlWriteMode.DiffGram) {
1489 SetTableRowsID (table);
1490 WriteDiffGramElement (writer);
1493 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1495 WriteTable (writer, table, mode, DataRowVersion.Default);
1497 if (mode == XmlWriteMode.DiffGram) {
1498 writer.WriteEndElement (); //DataSet name
1499 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1501 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1502 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1503 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1504 writer.WriteEndElement ();
1507 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1510 private void DoWriteXmlSchema (XmlWriter writer)
1512 if (writer.WriteState == WriteState.Start)
1513 writer.WriteStartDocument ();
1514 XmlSchemaWriter.WriteXmlSchema (this, writer);
1518 /// Helper function to split columns into attributes elements and simple
1521 internal static void SplitColumns (DataTable table,
1523 out ArrayList elements,
1524 out DataColumn simple)
1526 //The columns can be attributes, hidden, elements, or simple content
1527 //There can be 0-1 simple content cols or 0-* elements
1528 atts = new System.Collections.ArrayList ();
1529 elements = new System.Collections.ArrayList ();
1532 //Sort out the columns
1533 foreach (DataColumn col in table.Columns) {
1534 switch (col.ColumnMapping) {
1535 case MappingType.Attribute:
1538 case MappingType.Element:
1541 case MappingType.SimpleContent:
1542 if (simple != null) {
1543 throw new System.InvalidOperationException ("There may only be one simple content element");
1548 //ignore Hidden elements
1554 private void WriteDiffGramElement(XmlWriter writer)
1556 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1557 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1560 private void SetRowsID()
1562 foreach (DataTable Table in Tables)
1563 SetTableRowsID (Table);
1566 private void SetTableRowsID (DataTable Table)
1569 foreach (DataRow Row in Table.Rows) {
1570 Row.XmlRowID = dataRowID;
1574 #endregion //Private Xml Serialisation