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
756 * If document is diffgram we will use diffgram
758 if (r.LocalName == "diffgram")
759 return ReadXml (r, XmlReadMode.DiffGram);
762 * If we already have a schema, or the document
763 * contains an in-line schema, sets XmlReadMode to ReadSchema.
766 // FIXME: is this always true: "if we have tables we have to have schema also"
767 if (Tables.Count > 0)
768 return ReadXml (r, XmlReadMode.ReadSchema);
771 * If we dont have a schema yet and document
772 * contains no inline-schema mode is XmlReadMode.InferSchema
775 return ReadXml (r, XmlReadMode.InferSchema);
779 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
781 return ReadXml (new XmlTextReader (stream), mode);
784 public XmlReadMode ReadXml (string str, XmlReadMode mode)
786 return ReadXml (new XmlTextReader (str), mode);
789 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
791 return ReadXml (new XmlTextReader (reader), mode);
795 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
797 XmlReadMode Result = XmlReadMode.Auto;
799 if (mode == XmlReadMode.DiffGram) {
800 if (reader.LocalName != "diffgram"){
801 reader.MoveToContent ();
802 reader.ReadStartElement (); // <DataSet>
804 reader.MoveToContent ();
805 ReadXmlSchema (reader);
807 reader.MoveToContent ();
809 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
810 DiffLoader.Load (reader);
811 Result = XmlReadMode.DiffGram;
814 XmlDataLoader Loader = new XmlDataLoader (this);
815 Result = Loader.LoadData (reader, mode);
821 #endregion // Public Methods
823 #region Public Events
825 [DataCategory ("Action")]
826 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
827 public event MergeFailedEventHandler MergeFailed;
829 #endregion // Public Events
837 #endregion Destructors
839 #region IListSource methods
840 IList IListSource.GetList ()
842 return DefaultViewManager;
845 bool IListSource.ContainsListCollection {
850 #endregion IListSource methods
852 #region ISupportInitialize methods
853 public void BeginInit ()
857 public void EndInit ()
862 #region ISerializable
863 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
865 throw new NotImplementedException ();
869 #region Protected Methods
870 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
872 string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
873 if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
877 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
879 return BuildSchema ();
882 protected virtual void ReadXmlSerializable (XmlReader reader)
884 ReadXml (reader, XmlReadMode.DiffGram); // FIXME
887 void IXmlSerializable.ReadXml (XmlReader reader)
890 ReadXml (reader, XmlReadMode.DiffGram);
892 // the XmlSerializationReader does this lines!!!
893 //reader.MoveToContent ();
894 //reader.ReadEndElement (); // </DataSet>
897 void IXmlSerializable.WriteXml (XmlWriter writer)
899 DoWriteXmlSchema (writer);
900 WriteXml (writer, XmlWriteMode.DiffGram, true);
903 protected virtual bool ShouldSerializeRelations ()
908 protected virtual bool ShouldSerializeTables ()
914 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
919 protected virtual void OnRemoveRelation (DataRelation relation)
924 protected virtual void OnRemoveTable (DataTable table)
928 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
930 if (MergeFailed != null)
931 MergeFailed (this, e);
935 protected internal void RaisePropertyChanging (string name)
940 #region Private Xml Serialisation
942 private string WriteObjectXml (object o)
944 switch (Type.GetTypeCode (o.GetType ())) {
945 case TypeCode.Boolean:
946 return XmlConvert.ToString ((Boolean) o);
948 return XmlConvert.ToString ((Byte) o);
950 return XmlConvert.ToString ((Char) o);
951 case TypeCode.DateTime:
952 return XmlConvert.ToString ((DateTime) o);
953 case TypeCode.Decimal:
954 return XmlConvert.ToString ((Decimal) o);
955 case TypeCode.Double:
956 return XmlConvert.ToString ((Double) o);
958 return XmlConvert.ToString ((Int16) o);
960 return XmlConvert.ToString ((Int32) o);
962 return XmlConvert.ToString ((Int64) o);
964 return XmlConvert.ToString ((SByte) o);
965 case TypeCode.Single:
966 return XmlConvert.ToString ((Single) o);
967 case TypeCode.UInt16:
968 return XmlConvert.ToString ((UInt16) o);
969 case TypeCode.UInt32:
970 return XmlConvert.ToString ((UInt32) o);
971 case TypeCode.UInt64:
972 return XmlConvert.ToString ((UInt64) o);
974 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
975 if (o is Guid) return XmlConvert.ToString ((Guid) o);
976 return o.ToString ();
979 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
981 //Write out each table in order, providing it is not
982 //part of another table structure via a nested parent relationship
983 foreach (DataTable table in tableCollection) {
984 bool isTopLevel = true;
985 foreach (DataRelation rel in table.ParentRelations) {
993 WriteTable ( writer, table, mode, version);
998 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1000 DataRow[] rows = new DataRow [table.Rows.Count];
1001 table.Rows.CopyTo (rows, 0);
1002 WriteTable (writer, rows, mode, version);
1005 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1007 //The columns can be attributes, hidden, elements, or simple content
1008 //There can be 0-1 simple content cols or 0-* elements
1009 System.Collections.ArrayList atts;
1010 System.Collections.ArrayList elements;
1011 DataColumn simple = null;
1013 if (rows.Length == 0) return;
1014 DataTable table = rows[0].Table;
1015 SplitColumns (table, out atts, out elements, out simple);
1016 //sort out the namespacing
1017 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1019 foreach (DataRow row in rows) {
1020 if (!row.HasVersion(version))
1023 // First check are all the rows null. If they are we just write empty element
1024 bool AllNulls = true;
1025 foreach (DataColumn dc in table.Columns) {
1027 if (row [dc.ColumnName, version] != DBNull.Value) {
1033 // If all of the columns were null, we have to write empty element
1035 writer.WriteElementString (table.TableName, "");
1039 WriteTableElement (writer, mode, table, row, version);
1041 foreach (DataColumn col in atts) {
1042 WriteColumnAsAttribute (writer, mode, col, row, version);
1045 if (simple != null) {
1046 writer.WriteString (WriteObjectXml (row[simple, version]));
1049 foreach (DataColumn col in elements) {
1050 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1054 foreach (DataRelation relation in table.ChildRelations) {
1055 if (relation.Nested) {
1056 WriteTable (writer, row.GetChildRows (relation), mode, version);
1060 writer.WriteEndElement ();
1065 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1067 string colnspc = nspc;
1068 object rowObject = row [col, version];
1070 if (rowObject == null || rowObject == DBNull.Value)
1073 if (col.Namespace != null) {
1074 colnspc = col.Namespace;
1077 //TODO check if I can get away with write element string
1078 WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1079 writer.WriteString (WriteObjectXml (rowObject));
1080 writer.WriteEndElement ();
1083 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1085 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1088 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1090 //sort out the namespacing
1091 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1093 WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1095 if (mode == XmlWriteMode.DiffGram) {
1096 WriteAttributeString (writer, mode, "", "diffgr", "id", table.TableName + (row.XmlRowID + 1));
1097 WriteAttributeString (writer, mode, "", "msdata", "rowOrder", row.XmlRowID.ToString());
1098 if (row.RowState == DataRowState.Modified && version != DataRowVersion.Original){
1099 WriteAttributeString (writer, mode, "", "diffgr", "hasChanges", "modified");
1104 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1107 case XmlWriteMode.WriteSchema:
1108 if (nspc == null || nspc == "") {
1109 writer.WriteStartElement (name);
1111 else if (prefix != null) {
1112 writer.WriteStartElement (prefix, name, nspc);
1115 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1118 case XmlWriteMode.DiffGram:
1119 writer.WriteStartElement (name);
1122 writer.WriteStartElement (name);
1127 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1130 case XmlWriteMode.WriteSchema:
1131 writer.WriteAttributeString (prefix, name, nspc);
1133 case XmlWriteMode.DiffGram:
1134 writer.WriteAttributeString (prefix, name, nspc, stringValue);
1137 writer.WriteAttributeString (name, stringValue);
1142 XmlSchema IXmlSerializable.GetSchema ()
1144 return BuildSchema ();
1147 XmlSchema BuildSchema ()
1149 XmlSchema schema = new XmlSchema ();
1150 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1152 XmlSchemaElement elem = new XmlSchemaElement ();
1153 elem.Name = XmlConvert.EncodeName (DataSetName);
1155 XmlDocument doc = new XmlDocument ();
1157 XmlAttribute[] atts = new XmlAttribute [2];
1158 atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1159 atts[0].Value = "true";
1161 atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1162 atts[1].Value = locale.Name;
1163 elem.UnhandledAttributes = atts;
1165 schema.Items.Add (elem);
1167 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1168 elem.SchemaType = complex;
1170 XmlSchemaChoice choice = new XmlSchemaChoice ();
1171 complex.Particle = choice;
1172 choice.MaxOccursString = XmlConstants.Unbounded;
1174 //Write out schema for each table in order
1175 foreach (DataTable table in Tables) {
1176 bool isTopLevel = true;
1177 foreach (DataRelation rel in table.ParentRelations) {
1185 choice.Items.Add (GetTableSchema (doc, table));
1189 bool nameModifier = true;
1190 foreach (DataRelation rel in Relations) {
1191 XmlSchemaUnique uniq = new XmlSchemaUnique();
1192 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1193 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1194 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1197 uniq.Name = uqConst.ConstraintName;
1198 keyRef.Name = fkConst.ConstraintName;
1199 keyRef.Refer = new XmlQualifiedName(uniq.Name);
1200 XmlAttribute[] attrib = null;
1202 attrib = new XmlAttribute [2];
1203 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1204 attrib [0].Value = "true";
1206 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1207 attrib [1].Value = rel.RelationName;
1210 attrib = new XmlAttribute [1];
1211 attrib[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1212 attrib[0].Value = rel.RelationName;
1215 keyRef.UnhandledAttributes = attrib;
1216 nameModifier = false;
1219 uniq.Name = rel.ParentTable.TableName+"_"+uqConst.ConstraintName;
1220 keyRef.Name = rel.ChildTable.TableName+"_"+fkConst.ConstraintName;
1221 keyRef.Refer = new XmlQualifiedName(uniq.Name);
1222 XmlAttribute[] attrib;
1224 attrib = new XmlAttribute [3];
1225 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1226 attrib [0].Value = fkConst.ConstraintName;
1227 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1228 attrib [1].Value = "true";
1230 attrib [2] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1231 attrib [2].Value = rel.RelationName;
1234 attrib = new XmlAttribute [2];
1235 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1236 attrib [0].Value = fkConst.ConstraintName;
1237 attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
1238 attrib [1].Value = rel.RelationName;
1241 keyRef.UnhandledAttributes = attrib;
1242 attrib = new XmlAttribute [1];
1243 attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1244 attrib [0].Value = uqConst.ConstraintName;
1245 uniq.UnhandledAttributes = attrib;
1248 uniq.Selector = new XmlSchemaXPath();
1249 uniq.Selector.XPath = ".//"+rel.ParentTable.TableName;
1250 XmlSchemaXPath field;
1251 foreach (DataColumn column in rel.ParentColumns) {
1252 field = new XmlSchemaXPath();
1253 field.XPath = column.ColumnName;
1254 uniq.Fields.Add(field);
1257 keyRef.Selector = new XmlSchemaXPath();
1258 keyRef.Selector.XPath = ".//"+rel.ChildTable.TableName;
1259 foreach (DataColumn column in rel.ChildColumns) {
1260 field = new XmlSchemaXPath();
1261 field.XPath = column.ColumnName;
1262 keyRef.Fields.Add(field);
1265 elem.Constraints.Add (uniq);
1266 elem.Constraints.Add (keyRef);
1272 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1278 SplitColumns (table, out atts, out elements, out simple);
1280 XmlSchemaElement elem = new XmlSchemaElement ();
1281 elem.Name = table.TableName;
1283 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1284 elem.SchemaType = complex;
1286 //TODO - what about the simple content?
1287 if (elements.Count == 0) {
1290 //A sequence of element types or a simple content node
1292 XmlSchemaSequence seq = new XmlSchemaSequence ();
1293 complex.Particle = seq;
1295 foreach (DataColumn col in elements) {
1296 //<xs:element name=ColumnName type=MappedType Ordinal=index>
1297 XmlSchemaElement colElem = new XmlSchemaElement ();
1298 colElem.Name = col.ColumnName;
1300 if (col.ColumnName != col.Caption && col.Caption != string.Empty) {
1301 XmlAttribute[] xatts = new XmlAttribute[1];
1302 xatts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1303 xatts[0].Value = col.Caption;
1304 colElem.UnhandledAttributes = xatts;
1307 if (col.DefaultValue.ToString () != string.Empty)
1308 colElem.DefaultValue = col.DefaultValue.ToString ();
1310 colElem.SchemaTypeName = MapType (col.DataType);
1312 if (col.AllowDBNull) {
1313 colElem.MinOccurs = 0;
1316 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1317 // XmlConstants.Ordinal,
1318 // XmlConstants.MsdataNamespace,
1319 // col.Ordinal.ToString ());
1321 // Write SimpleType if column have MaxLength
1322 if (col.MaxLength > -1) {
1323 colElem.SchemaType = GetTableSimpleType (doc, col);
1326 seq.Items.Add (colElem);
1329 foreach (DataRelation rel in table.ChildRelations) {
1331 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1336 //Then a list of attributes
1337 foreach (DataColumn col in atts) {
1338 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1339 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1340 att.Name = col.ColumnName;
1341 att.Form = XmlSchemaForm.Unqualified;
1342 att.SchemaTypeName = MapType (col.DataType);
1343 complex.Attributes.Add (att);
1349 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1352 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1355 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1356 restriction.BaseTypeName = MapType (col.DataType);
1359 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1360 max.Value = XmlConvert.ToString (col.MaxLength);
1361 restriction.Facets.Add (max);
1366 private void DoWriteXmlSchema (XmlWriter writer)
1368 GetSchemaSerializable ().Write (writer);
1372 /// Helper function to split columns into attributes elements and simple
1375 private void SplitColumns (DataTable table,
1377 out ArrayList elements,
1378 out DataColumn simple)
1380 //The columns can be attributes, hidden, elements, or simple content
1381 //There can be 0-1 simple content cols or 0-* elements
1382 atts = new System.Collections.ArrayList ();
1383 elements = new System.Collections.ArrayList ();
1386 //Sort out the columns
1387 foreach (DataColumn col in table.Columns) {
1388 switch (col.ColumnMapping) {
1389 case MappingType.Attribute:
1392 case MappingType.Element:
1395 case MappingType.SimpleContent:
1396 if (simple != null) {
1397 throw new System.InvalidOperationException ("There may only be one simple content element");
1402 //ignore Hidden elements
1408 private void WriteDiffGramElement(XmlWriter writer)
1410 WriteStartElement (writer, XmlWriteMode.DiffGram, Namespace, Prefix, "diffgr:diffgram");
1411 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1412 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.DiffgrPrefix, XmlConstants.DiffgrNamespace);
1415 private void SetRowsID()
1417 foreach (DataTable Table in Tables) {
1419 foreach (DataRow Row in Table.Rows) {
1420 Row.XmlRowID = dataRowID;
1427 private XmlQualifiedName MapType (Type type)
1429 switch (Type.GetTypeCode (type)) {
1430 case TypeCode.String: return XmlConstants.QnString;
1431 case TypeCode.Int16: return XmlConstants.QnShort;
1432 case TypeCode.Int32: return XmlConstants.QnInt;
1433 case TypeCode.Int64: return XmlConstants.QnLong;
1434 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1435 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1436 case TypeCode.Char: return XmlConstants.QnChar;
1437 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1438 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1439 case TypeCode.Double: return XmlConstants.QnDouble;
1440 case TypeCode.SByte: return XmlConstants.QnSbyte;
1441 case TypeCode.Single: return XmlConstants.QnFloat;
1442 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1443 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1444 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1447 if (typeof (TimeSpan) == type)
1448 return XmlConstants.QnDuration;
1449 else if (typeof (System.Uri) == type)
1450 return XmlConstants.QnUri;
1451 else if (typeof (byte[]) == type)
1452 return XmlConstants.QnBase64Binary;
1453 else if (typeof (XmlQualifiedName) == type)
1454 return XmlConstants.QnXmlQualifiedName;
1456 return XmlConstants.QnString;
1459 #endregion //Private Xml Serialisation