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>
12 // (C) Ximian, Inc. 2002
13 // Copyright (C) Tim Coleman, 2002, 2003
17 using System.Collections;
18 using System.ComponentModel;
19 using System.Globalization;
20 using System.Threading;
22 using System.Runtime.Serialization;
24 using System.Xml.Schema;
25 using System.Xml.Serialization;
26 using System.Data.Common;
28 namespace System.Data {
31 [DefaultProperty ("DataSetName")]
33 public class DataSet : MarshalByValueComponent, IListSource,
34 ISupportInitialize, ISerializable, IXmlSerializable
36 private string dataSetName;
37 private string _namespace = "";
38 private string prefix;
39 private bool caseSensitive;
40 private bool enforceConstraints = true;
41 private DataTableCollection tableCollection;
42 private DataRelationCollection relationCollection;
43 private PropertyCollection properties;
44 private DataViewManager defaultView;
45 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
49 public DataSet () : this ("NewDataSet")
53 public DataSet (string name)
56 tableCollection = new DataTableCollection (this);
57 relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
58 properties = new PropertyCollection ();
59 this.prefix = String.Empty;
61 this.Locale = CultureInfo.CurrentCulture;
65 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
67 throw new NotImplementedException ();
70 #endregion // Constructors
72 #region Public Properties
74 [DataCategory ("Data")]
75 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
76 [DefaultValue (false)]
77 public bool CaseSensitive {
82 foreach (DataTable T in Tables) {
83 if (T.VirginCaseSensitive)
84 T.CaseSensitive = value;
87 caseSensitive = value;
89 foreach (DataTable table in Tables) {
90 foreach (Constraint c in table.Constraints)
91 c.AssertConstraint ();
97 [DataCategory ("Data")]
98 [DataSysDescription ("The name of this DataSet.")]
100 public string DataSetName {
101 get { return dataSetName; }
102 set { dataSetName = value; }
105 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
107 public DataViewManager DefaultViewManager {
109 if (defaultView == null)
110 defaultView = new DataViewManager (this);
115 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
116 [DefaultValue (true)]
117 public bool EnforceConstraints {
118 get { return enforceConstraints; }
120 if (value != enforceConstraints) {
121 enforceConstraints = value;
123 foreach (DataTable table in Tables) {
124 foreach (Constraint c in table.Constraints)
125 c.AssertConstraint ();
133 [DataCategory ("Data")]
134 [DataSysDescription ("The collection that holds custom user information.")]
135 public PropertyCollection ExtendedProperties {
136 get { return properties; }
140 [DataSysDescription ("Indicates that the DataSet has errors.")]
141 public bool HasErrors {
144 for (int i = 0; i < Tables.Count; i++) {
145 if (Tables[i].HasErrors)
152 [DataCategory ("Data")]
153 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
154 public CultureInfo Locale {
159 if (locale == null || !locale.Equals (value)) {
160 // TODO: check if the new locale is valid
161 // TODO: update locale of all tables
167 public void Merge (DataRow[] rows)
169 Merge (rows, false, MissingSchemaAction.Add);
172 public void Merge (DataSet dataSet)
174 Merge (dataSet, false, MissingSchemaAction.Add);
177 public void Merge (DataTable table)
179 Merge (table, false, MissingSchemaAction.Add);
182 public void Merge (DataSet dataSet, bool preserveChanges)
184 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
188 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
191 throw new ArgumentNullException ("rows");
192 if (!IsLegalSchemaAction (missingSchemaAction))
193 throw new ArgumentOutOfRangeException ("missingSchemaAction");
195 MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
199 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
202 throw new ArgumentNullException ("dataSet");
203 if (!IsLegalSchemaAction (missingSchemaAction))
204 throw new ArgumentOutOfRangeException ("missingSchemaAction");
206 MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
210 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
213 throw new ArgumentNullException ("table");
214 if (!IsLegalSchemaAction (missingSchemaAction))
215 throw new ArgumentOutOfRangeException ("missingSchemaAction");
217 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
220 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
222 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
223 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
228 [DataCategory ("Data")]
229 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
231 public string Namespace {
233 get { return _namespace; }
236 //TODO - trigger an event if this happens?
241 [DataCategory ("Data")]
242 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
244 public string Prefix {
246 get { return prefix; }
249 //TODO - trigger an event if this happens?
252 value = string.Empty;
254 if (value != this.prefix)
255 RaisePropertyChanging ("Prefix");
260 [DataCategory ("Data")]
261 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
262 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
263 public DataRelationCollection Relations {
265 return relationCollection;
270 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
271 public override ISite Site {
274 throw new NotImplementedException ();
279 throw new NotImplementedException ();
283 [DataCategory ("Data")]
284 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
285 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
286 public DataTableCollection Tables {
287 get { return tableCollection; }
290 #endregion // Public Properties
292 #region Public Methods
295 public void AcceptChanges ()
297 foreach (DataTable tempTable in tableCollection)
298 tempTable.AcceptChanges ();
303 // TODO: if currently bound to a XmlDataDocument
304 // throw a NotSupportedException
305 for (int t = 0; t < tableCollection.Count; t++) {
306 tableCollection[t].Clear ();
310 public virtual DataSet Clone ()
312 DataSet Copy = new DataSet ();
313 CopyProperties (Copy);
315 foreach (DataTable Table in Tables) {
316 Copy.Tables.Add (Table.Clone ());
319 //Copy Relationships between tables after existance of tables
320 //and setting properties correctly
321 CopyRelations (Copy);
326 // Copies both the structure and data for this DataSet.
327 public DataSet Copy ()
329 DataSet Copy = new DataSet ();
330 CopyProperties (Copy);
332 // Copy DatSet's tables
333 foreach (DataTable Table in Tables)
334 Copy.Tables.Add (Table.Copy ());
336 //Copy Relationships between tables after existance of tables
337 //and setting properties correctly
338 CopyRelations (Copy);
344 private void CopyProperties (DataSet Copy)
346 Copy.CaseSensitive = CaseSensitive;
347 //Copy.Container = Container
348 Copy.DataSetName = DataSetName;
349 //Copy.DefaultViewManager
351 Copy.EnforceConstraints = EnforceConstraints;
352 //Copy.ExtendedProperties
354 //Copy.Locale = Locale;
355 Copy.Namespace = Namespace;
356 Copy.Prefix = Prefix;
362 private void CopyRelations (DataSet Copy)
365 //Creation of the relation contains some of the properties, and the constructor
366 //demands these values. instead changing the DataRelation constructor and behaviour the
367 //parameters are pre-configured and sent to the most general constructor
369 foreach (DataRelation MyRelation in this.Relations) {
370 string pTable = MyRelation.ParentTable.TableName;
371 string cTable = MyRelation.ChildTable.TableName;
372 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
373 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
376 foreach (DataColumn DC in MyRelation.ParentColumns) {
377 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
383 foreach (DataColumn DC in MyRelation.ChildColumns) {
384 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
388 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
389 //cRel.ChildColumns = MyRelation.ChildColumns;
390 //cRel.ChildTable = MyRelation.ChildTable;
391 //cRel.ExtendedProperties = cRel.ExtendedProperties;
392 //cRel.Nested = MyRelation.Nested;
393 //cRel.ParentColumns = MyRelation.ParentColumns;
394 //cRel.ParentTable = MyRelation.ParentTable;
396 Copy.Relations.Add (cRel);
403 public DataSet GetChanges ()
405 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
409 public DataSet GetChanges (DataRowState rowStates)
411 if (!HasChanges (rowStates))
414 DataSet copySet = Clone ();
415 IEnumerator tableEnumerator = Tables.GetEnumerator ();
418 while (tableEnumerator.MoveNext ()) {
419 origTable = (DataTable)tableEnumerator.Current;
420 copyTable = copySet.Tables[origTable.TableName];
422 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
423 while (rowEnumerator.MoveNext ()) {
424 DataRow row = (DataRow)rowEnumerator.Current;
425 if (row.IsRowChanged (rowStates)) {
426 DataRow newRow = copyTable.NewRow ();
427 copyTable.Rows.Add (newRow);
428 row.CopyValuesToRow (newRow);
437 public DataTableReader GetDataReader (DataTable[] dataTables)
439 throw new NotImplementedException ();
443 public DataTableReader GetDataReader ()
445 throw new NotImplementedException ();
449 public string GetXml ()
451 StringWriter Writer = new StringWriter ();
453 // Sending false for not printing the Processing instruction
454 WriteXml (Writer, XmlWriteMode.IgnoreSchema, false);
455 return Writer.ToString ();
458 public string GetXmlSchema ()
460 StringWriter Writer = new StringWriter ();
461 WriteXmlSchema (Writer);
462 return Writer.ToString ();
466 public bool HasChanges ()
468 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
472 public bool HasChanges (DataRowState rowState)
474 if (((int)rowState & 0xffffffe0) != 0)
475 throw new ArgumentOutOfRangeException ("rowState");
477 DataTableCollection tableCollection = Tables;
479 DataRowCollection rowCollection;
482 for (int i = 0; i < tableCollection.Count; i++) {
483 table = tableCollection[i];
484 rowCollection = table.Rows;
485 for (int j = 0; j < rowCollection.Count; j++) {
486 row = rowCollection[j];
487 if ((row.RowState & rowState) != 0)
496 public void InferXmlSchema (XmlReader reader, string[] nsArray)
500 public void InferXmlSchema (Stream stream, string[] nsArray)
502 InferXmlSchema (new XmlTextReader (stream), nsArray);
505 public void InferXmlSchema (TextReader reader, string[] nsArray)
507 InferXmlSchema (new XmlTextReader (reader), nsArray);
510 public void InferXmlSchema (string fileName, string[] nsArray)
512 XmlTextReader reader = new XmlTextReader (fileName);
514 InferXmlSchema (reader, nsArray);
522 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
524 throw new NotImplementedException ();
528 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
530 throw new NotImplementedException ();
534 public virtual void RejectChanges ()
537 bool oldEnforceConstraints = this.EnforceConstraints;
538 this.EnforceConstraints = false;
540 for (i = 0; i < this.Tables.Count;i++)
541 this.Tables[i].RejectChanges ();
543 this.EnforceConstraints = oldEnforceConstraints;
546 public virtual void Reset ()
548 IEnumerator constraintEnumerator;
550 // first we remove all ForeignKeyConstraints (if we will not do that
551 // we will get an exception when clearing the tables).
552 for (int i = 0; i < Tables.Count; i++) {
553 ConstraintCollection cc = Tables[i].Constraints;
554 for (int j = 0; j < cc.Count; j++) {
555 if (cc[j] is ForeignKeyConstraint)
565 public void WriteXml (Stream stream)
567 XmlWriter writer = new XmlTextWriter (stream, null);
573 /// Writes the current data for the DataSet to the specified file.
575 /// <param name="filename">Fully qualified filename to write to</param>
576 public void WriteXml (string fileName)
578 XmlWriter writer = new XmlTextWriter (fileName, null);
585 public void WriteXml (TextWriter writer)
587 XmlWriter xwriter = new XmlTextWriter (writer);
592 public void WriteXml (XmlWriter writer)
594 WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
597 public void WriteXml (string filename, XmlWriteMode mode)
599 XmlWriter writer = new XmlTextWriter (filename, null);
600 WriteXml (writer, mode, true);
603 public void WriteXml (Stream stream, XmlWriteMode mode)
605 XmlWriter writer = new XmlTextWriter (stream, null);
607 WriteXml (writer, mode, true);
610 public void WriteXml (TextWriter writer, XmlWriteMode mode)
612 XmlWriter xwriter = new XmlTextWriter (writer);
613 WriteXml (xwriter, mode, true);
616 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
618 WriteXml (writer, mode, true);
621 internal void WriteXml (Stream stream, XmlWriteMode mode, bool writePI)
623 XmlWriter writer = new XmlTextWriter (stream, null);
625 WriteXml (writer, mode, writePI);
628 internal void WriteXml (string fileName, XmlWriteMode mode, bool writePI)
630 XmlWriter writer = new XmlTextWriter (fileName, null);
632 WriteXml (writer, mode, writePI);
637 internal void WriteXml (TextWriter writer, XmlWriteMode mode, bool writePI)
639 XmlWriter xwriter = new XmlTextWriter (writer);
641 WriteXml (xwriter, mode, writePI);
644 internal void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writePI)
646 if (writePI && (writer.WriteState == WriteState.Start))
647 writer.WriteStartDocument (true);
649 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
651 if (mode == XmlWriteMode.DiffGram) {
653 WriteDiffGramElement(writer);
656 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
658 if (mode == XmlWriteMode.WriteSchema) {
659 DoWriteXmlSchema (writer);
662 WriteTables (writer, mode, Tables, DataRowVersion.Default);
663 if (mode == XmlWriteMode.DiffGram) {
664 writer.WriteEndElement (); //DataSet name
665 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
667 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
668 WriteStartElement (writer, XmlWriteMode.DiffGram, Namespace, Prefix, "diffgr:before");
669 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
670 writer.WriteEndElement ();
673 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
676 public void WriteXmlSchema (Stream stream)
678 XmlWriter writer = new XmlTextWriter (stream, null );
680 WriteXmlSchema (writer);
683 public void WriteXmlSchema (string fileName)
685 XmlWriter writer = new XmlTextWriter (fileName, null);
687 WriteXmlSchema (writer);
690 public void WriteXmlSchema (TextWriter writer)
692 XmlWriter xwriter = new XmlTextWriter (writer);
694 WriteXmlSchema (xwriter);
697 public void WriteXmlSchema (XmlWriter writer)
699 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
700 //Create a skeleton doc and then write the schema
701 //proper which is common to the WriteXml method in schema mode
702 writer.WriteStartDocument ();
704 DoWriteXmlSchema (writer);
706 writer.WriteEndDocument ();
709 public void ReadXmlSchema (Stream stream)
711 XmlReader reader = new XmlTextReader (stream, null);
712 ReadXmlSchema (reader);
715 public void ReadXmlSchema (string str)
717 XmlReader reader = new XmlTextReader (str);
718 ReadXmlSchema (reader);
721 public void ReadXmlSchema (TextReader treader)
723 XmlReader reader = new XmlTextReader (treader);
724 ReadXmlSchema (reader);
727 public void ReadXmlSchema (XmlReader reader)
729 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
730 SchemaMapper.Read (reader);
733 public XmlReadMode ReadXml (Stream stream)
735 return ReadXml (new XmlTextReader (stream));
738 public XmlReadMode ReadXml (string str)
740 return ReadXml (new XmlTextReader (str));
743 public XmlReadMode ReadXml (TextReader reader)
745 return ReadXml (new XmlTextReader (reader));
748 public XmlReadMode ReadXml (XmlReader r)
750 XmlDataLoader Loader = new XmlDataLoader (this);
751 // FIXME: somekinda exception?
753 return XmlReadMode.Auto; // FIXME
755 // Check if the curent element is the process instruction (PI).
756 // if it is move to next element.
757 if (r.LocalName == "xml")
761 * If document is diffgram we will use diffgram
763 if (r.LocalName == "diffgram")
764 return ReadXml (r, XmlReadMode.DiffGram);
766 // Get the DataSet name.
767 string dataSetName = XmlConvert.DecodeName (r.LocalName);
768 DataSetName = dataSetName;
770 r.ReadStartElement ();
773 bool schemaRead = false;
774 // Check if the current element is the schema
775 if (r.LocalName == "schema") {
781 if (r.LocalName == "diffgram") {
782 return ReadXml (r, XmlReadMode.DiffGram);
785 // If the schema has been read we should read the rest of the document
787 ReadXml (r, XmlReadMode.IgnoreSchema, false);
788 return XmlReadMode.ReadSchema;
791 // Read with inferschema.
792 return ReadXml (r, XmlReadMode.InferSchema, false);
796 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
798 return ReadXml (new XmlTextReader (stream), mode);
801 public XmlReadMode ReadXml (string str, XmlReadMode mode)
803 return ReadXml (new XmlTextReader (str), mode);
806 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
808 return ReadXml (new XmlTextReader (reader), mode);
812 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
814 // we have to initiate the reader.
815 if (reader.ReadState == ReadState.Initial)
818 // Check if the curent element is the process instruction (PI).
819 // if it is move to next element.
820 if (reader.LocalName == "xml")
821 reader.MoveToContent();
823 XmlReadMode Result = XmlReadMode.Auto;
825 if (mode == XmlReadMode.DiffGram) {
826 if (reader.LocalName != "diffgram"){
827 reader.MoveToContent ();
828 reader.ReadStartElement (); // <DataSet>
830 reader.MoveToContent ();
831 if (reader.LocalName == "schema")
832 ReadXmlSchema (reader);
834 reader.MoveToContent ();
836 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
837 DiffLoader.Load (reader);
838 Result = XmlReadMode.DiffGram;
841 Result = ReadXml(reader, mode, true);
846 private XmlReadMode ReadXml (XmlReader r, XmlReadMode mode, bool readDataSet) {
849 string dataSetName = XmlConvert.DecodeName (r.LocalName);
850 DataSetName = dataSetName;
851 // get the Namespace of the DataSet.
852 string tmp = r.GetAttribute("xmlns");
856 r.ReadStartElement ();
860 XmlDataLoader Loader = new XmlDataLoader (this);
861 return Loader.LoadData (r, mode);
864 #endregion // Public Methods
866 #region Public Events
868 [DataCategory ("Action")]
869 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
870 public event MergeFailedEventHandler MergeFailed;
872 #endregion // Public Events
880 #endregion Destructors
882 #region IListSource methods
883 IList IListSource.GetList ()
885 return DefaultViewManager;
888 bool IListSource.ContainsListCollection {
893 #endregion IListSource methods
895 #region ISupportInitialize methods
896 public void BeginInit ()
900 public void EndInit ()
905 #region ISerializable
906 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
908 throw new NotImplementedException ();
912 #region Protected Methods
913 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
915 string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
916 if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
920 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
922 return BuildSchema ();
925 protected virtual void ReadXmlSerializable (XmlReader reader)
927 ReadXml (reader, XmlReadMode.DiffGram); // FIXME
930 void IXmlSerializable.ReadXml (XmlReader reader)
933 ReadXmlSerializable(reader);
935 // the XmlSerializationReader does this lines!!!
936 //reader.MoveToContent ();
937 //reader.ReadEndElement (); // </DataSet>
940 void IXmlSerializable.WriteXml (XmlWriter writer)
942 DoWriteXmlSchema (writer);
943 WriteXml (writer, XmlWriteMode.DiffGram, true);
946 protected virtual bool ShouldSerializeRelations ()
951 protected virtual bool ShouldSerializeTables ()
957 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
962 protected virtual void OnRemoveRelation (DataRelation relation)
967 protected virtual void OnRemoveTable (DataTable table)
971 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
973 if (MergeFailed != null)
974 MergeFailed (this, e);
978 protected internal void RaisePropertyChanging (string name)
983 #region Private Xml Serialisation
985 private string WriteObjectXml (object o)
987 switch (Type.GetTypeCode (o.GetType ())) {
988 case TypeCode.Boolean:
989 return XmlConvert.ToString ((Boolean) o);
991 return XmlConvert.ToString ((Byte) o);
993 return XmlConvert.ToString ((Char) o);
994 case TypeCode.DateTime:
995 return XmlConvert.ToString ((DateTime) o);
996 case TypeCode.Decimal:
997 return XmlConvert.ToString ((Decimal) o);
998 case TypeCode.Double:
999 return XmlConvert.ToString ((Double) o);
1000 case TypeCode.Int16:
1001 return XmlConvert.ToString ((Int16) o);
1002 case TypeCode.Int32:
1003 return XmlConvert.ToString ((Int32) o);
1004 case TypeCode.Int64:
1005 return XmlConvert.ToString ((Int64) o);
1006 case TypeCode.SByte:
1007 return XmlConvert.ToString ((SByte) o);
1008 case TypeCode.Single:
1009 return XmlConvert.ToString ((Single) o);
1010 case TypeCode.UInt16:
1011 return XmlConvert.ToString ((UInt16) o);
1012 case TypeCode.UInt32:
1013 return XmlConvert.ToString ((UInt32) o);
1014 case TypeCode.UInt64:
1015 return XmlConvert.ToString ((UInt64) o);
1017 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1018 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1019 return o.ToString ();
1022 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1024 //Write out each table in order, providing it is not
1025 //part of another table structure via a nested parent relationship
1026 foreach (DataTable table in tableCollection) {
1027 bool isTopLevel = true;
1028 foreach (DataRelation rel in table.ParentRelations) {
1036 WriteTable ( writer, table, mode, version);
1041 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1043 DataRow[] rows = new DataRow [table.Rows.Count];
1044 table.Rows.CopyTo (rows, 0);
1045 WriteTable (writer, rows, mode, version);
1048 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1050 //The columns can be attributes, hidden, elements, or simple content
1051 //There can be 0-1 simple content cols or 0-* elements
1052 System.Collections.ArrayList atts;
1053 System.Collections.ArrayList elements;
1054 DataColumn simple = null;
1056 if (rows.Length == 0) return;
1057 DataTable table = rows[0].Table;
1058 SplitColumns (table, out atts, out elements, out simple);
1059 //sort out the namespacing
1060 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1062 foreach (DataRow row in rows) {
1063 if (!row.HasVersion(version))
1066 // First check are all the rows null. If they are we just write empty element
1067 bool AllNulls = true;
1068 foreach (DataColumn dc in table.Columns) {
1070 if (row [dc.ColumnName, version] != DBNull.Value) {
1076 // If all of the columns were null, we have to write empty element
1078 writer.WriteElementString (table.TableName, "");
1082 WriteTableElement (writer, mode, table, row, version);
1084 foreach (DataColumn col in atts) {
1085 WriteColumnAsAttribute (writer, mode, col, row, version);
1088 if (simple != null) {
1089 writer.WriteString (WriteObjectXml (row[simple, version]));
1092 foreach (DataColumn col in elements) {
1093 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1097 foreach (DataRelation relation in table.ChildRelations) {
1098 if (relation.Nested) {
1099 WriteTable (writer, row.GetChildRows (relation), mode, version);
1103 writer.WriteEndElement ();
1108 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1110 string colnspc = nspc;
1111 object rowObject = row [col, version];
1113 if (rowObject == null || rowObject == DBNull.Value)
1116 if (col.Namespace != null) {
1117 colnspc = col.Namespace;
1120 //TODO check if I can get away with write element string
1121 WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1122 writer.WriteString (WriteObjectXml (rowObject));
1123 writer.WriteEndElement ();
1126 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1128 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1131 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1133 //sort out the namespacing
1134 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1136 WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1138 if (mode == XmlWriteMode.DiffGram) {
1139 WriteAttributeString (writer, mode, "", "diffgr", "id", table.TableName + (row.XmlRowID + 1));
1140 WriteAttributeString (writer, mode, "", "msdata", "rowOrder", row.XmlRowID.ToString());
1141 if (row.RowState == DataRowState.Modified && version != DataRowVersion.Original){
1142 WriteAttributeString (writer, mode, "", "diffgr", "hasChanges", "modified");
1147 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1150 case XmlWriteMode.WriteSchema:
1151 if (nspc == null || nspc == "") {
1152 writer.WriteStartElement (name);
1154 else if (prefix != null) {
1155 writer.WriteStartElement (prefix, name, nspc);
1158 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1161 case XmlWriteMode.DiffGram:
1162 writer.WriteStartElement (name);
1165 writer.WriteStartElement (name);
1170 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1173 case XmlWriteMode.WriteSchema:
1174 writer.WriteAttributeString (prefix, name, nspc);
1176 case XmlWriteMode.DiffGram:
1177 writer.WriteAttributeString (prefix, name, nspc, stringValue);
1180 writer.WriteAttributeString (name, stringValue);
1185 XmlSchema IXmlSerializable.GetSchema ()
1187 return BuildSchema ();
1190 XmlSchema BuildSchema ()
1192 XmlSchema schema = new XmlSchema ();
1193 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1195 XmlSchemaElement elem = new XmlSchemaElement ();
1196 elem.Name = XmlConvert.EncodeName (DataSetName);
1198 XmlDocument doc = new XmlDocument ();
1200 XmlAttribute[] atts = new XmlAttribute [2];
1201 atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1202 atts[0].Value = "true";
1204 atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1205 atts[1].Value = locale.Name;
1206 elem.UnhandledAttributes = atts;
1208 schema.Items.Add (elem);
1210 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1211 elem.SchemaType = complex;
1213 XmlSchemaChoice choice = new XmlSchemaChoice ();
1214 complex.Particle = choice;
1215 choice.MaxOccursString = XmlConstants.Unbounded;
1217 //Write out schema for each table in order
1218 foreach (DataTable table in Tables) {
1219 bool isTopLevel = true;
1220 foreach (DataRelation rel in table.ParentRelations) {
1228 choice.Items.Add (GetTableSchema (doc, table));
1232 bool nameModifier = true;
1233 foreach (DataRelation rel in Relations) {
1234 XmlSchemaUnique uniq = new XmlSchemaUnique();
1235 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1236 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1237 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1240 uniq.Name = uqConst.ConstraintName;
1241 keyRef.Name = fkConst.ConstraintName;
1242 keyRef.Refer = new XmlQualifiedName(uniq.Name);
1243 XmlAttribute[] attrib = null;
1245 attrib = new XmlAttribute [2];
1246 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1247 attrib [0].Value = "true";
1249 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1250 attrib [1].Value = rel.RelationName;
1253 attrib = new XmlAttribute [1];
1254 attrib[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1255 attrib[0].Value = rel.RelationName;
1258 keyRef.UnhandledAttributes = attrib;
1259 nameModifier = false;
1262 uniq.Name = rel.ParentTable.TableName+"_"+uqConst.ConstraintName;
1263 keyRef.Name = rel.ChildTable.TableName+"_"+fkConst.ConstraintName;
1264 keyRef.Refer = new XmlQualifiedName(uniq.Name);
1265 XmlAttribute[] attrib;
1267 attrib = new XmlAttribute [3];
1268 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1269 attrib [0].Value = fkConst.ConstraintName;
1270 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1271 attrib [1].Value = "true";
1273 attrib [2] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1274 attrib [2].Value = rel.RelationName;
1277 attrib = new XmlAttribute [2];
1278 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1279 attrib [0].Value = fkConst.ConstraintName;
1280 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1281 attrib [1].Value = rel.RelationName;
1284 keyRef.UnhandledAttributes = attrib;
1285 attrib = new XmlAttribute [1];
1286 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1287 attrib [0].Value = uqConst.ConstraintName;
1288 uniq.UnhandledAttributes = attrib;
1291 uniq.Selector = new XmlSchemaXPath();
1292 uniq.Selector.XPath = ".//"+rel.ParentTable.TableName;
1293 XmlSchemaXPath field;
1294 foreach (DataColumn column in rel.ParentColumns) {
1295 field = new XmlSchemaXPath();
1296 field.XPath = column.ColumnName;
1297 uniq.Fields.Add(field);
1300 keyRef.Selector = new XmlSchemaXPath();
1301 keyRef.Selector.XPath = ".//"+rel.ChildTable.TableName;
1302 foreach (DataColumn column in rel.ChildColumns) {
1303 field = new XmlSchemaXPath();
1304 field.XPath = column.ColumnName;
1305 keyRef.Fields.Add(field);
1308 elem.Constraints.Add (uniq);
1309 elem.Constraints.Add (keyRef);
1315 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1321 SplitColumns (table, out atts, out elements, out simple);
1323 XmlSchemaElement elem = new XmlSchemaElement ();
1324 elem.Name = table.TableName;
1326 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1327 elem.SchemaType = complex;
1329 //TODO - what about the simple content?
1330 if (elements.Count == 0) {
1333 //A sequence of element types or a simple content node
1335 XmlSchemaSequence seq = new XmlSchemaSequence ();
1336 complex.Particle = seq;
1338 foreach (DataColumn col in elements) {
1339 //<xs:element name=ColumnName type=MappedType Ordinal=index>
1340 XmlSchemaElement colElem = new XmlSchemaElement ();
1341 colElem.Name = col.ColumnName;
1343 if (col.ColumnName != col.Caption && col.Caption != string.Empty) {
1344 XmlAttribute[] xatts = new XmlAttribute[1];
1345 xatts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1346 xatts[0].Value = col.Caption;
1347 colElem.UnhandledAttributes = xatts;
1350 if (col.DefaultValue.ToString () != string.Empty)
1351 colElem.DefaultValue = col.DefaultValue.ToString ();
1353 colElem.SchemaTypeName = MapType (col.DataType);
1355 if (col.AllowDBNull) {
1356 colElem.MinOccurs = 0;
1359 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1360 // XmlConstants.Ordinal,
1361 // XmlConstants.MsdataNamespace,
1362 // col.Ordinal.ToString ());
1364 // Write SimpleType if column have MaxLength
1365 if (col.MaxLength > -1) {
1366 colElem.SchemaType = GetTableSimpleType (doc, col);
1369 seq.Items.Add (colElem);
1372 foreach (DataRelation rel in table.ChildRelations) {
1374 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1379 //Then a list of attributes
1380 foreach (DataColumn col in atts) {
1381 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1382 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1383 att.Name = col.ColumnName;
1384 att.Form = XmlSchemaForm.Unqualified;
1385 att.SchemaTypeName = MapType (col.DataType);
1386 complex.Attributes.Add (att);
1392 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1395 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1398 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1399 restriction.BaseTypeName = MapType (col.DataType);
1402 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1403 max.Value = XmlConvert.ToString (col.MaxLength);
1404 restriction.Facets.Add (max);
1409 private void DoWriteXmlSchema (XmlWriter writer)
1411 GetSchemaSerializable ().Write (writer);
1415 /// Helper function to split columns into attributes elements and simple
1418 private void SplitColumns (DataTable table,
1420 out ArrayList elements,
1421 out DataColumn simple)
1423 //The columns can be attributes, hidden, elements, or simple content
1424 //There can be 0-1 simple content cols or 0-* elements
1425 atts = new System.Collections.ArrayList ();
1426 elements = new System.Collections.ArrayList ();
1429 //Sort out the columns
1430 foreach (DataColumn col in table.Columns) {
1431 switch (col.ColumnMapping) {
1432 case MappingType.Attribute:
1435 case MappingType.Element:
1438 case MappingType.SimpleContent:
1439 if (simple != null) {
1440 throw new System.InvalidOperationException ("There may only be one simple content element");
1445 //ignore Hidden elements
1451 private void WriteDiffGramElement(XmlWriter writer)
1453 WriteStartElement (writer, XmlWriteMode.DiffGram, Namespace, Prefix, "diffgr:diffgram");
1454 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1455 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.DiffgrPrefix, XmlConstants.DiffgrNamespace);
1458 private void SetRowsID()
1460 foreach (DataTable Table in Tables) {
1462 foreach (DataRow Row in Table.Rows) {
1463 Row.XmlRowID = dataRowID;
1470 private XmlQualifiedName MapType (Type type)
1472 switch (Type.GetTypeCode (type)) {
1473 case TypeCode.String: return XmlConstants.QnString;
1474 case TypeCode.Int16: return XmlConstants.QnShort;
1475 case TypeCode.Int32: return XmlConstants.QnInt;
1476 case TypeCode.Int64: return XmlConstants.QnLong;
1477 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1478 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1479 case TypeCode.Char: return XmlConstants.QnChar;
1480 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1481 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1482 case TypeCode.Double: return XmlConstants.QnDouble;
1483 case TypeCode.SByte: return XmlConstants.QnSbyte;
1484 case TypeCode.Single: return XmlConstants.QnFloat;
1485 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1486 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1487 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1490 if (typeof (TimeSpan) == type)
1491 return XmlConstants.QnDuration;
1492 else if (typeof (System.Uri) == type)
1493 return XmlConstants.QnUri;
1494 else if (typeof (byte[]) == type)
1495 return XmlConstants.QnBase64Binary;
1496 else if (typeof (XmlQualifiedName) == type)
1497 return XmlConstants.QnXmlQualifiedName;
1499 return XmlConstants.QnString;
1502 #endregion //Private Xml Serialisation