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 DataSet Copy = new DataSet ();
351 CopyProperties (Copy);
353 foreach (DataTable Table in Tables) {
354 Copy.Tables.Add (Table.Clone ());
357 //Copy Relationships between tables after existance of tables
358 //and setting properties correctly
359 CopyRelations (Copy);
364 // Copies both the structure and data for this DataSet.
365 public DataSet Copy ()
367 DataSet Copy = new DataSet ();
368 CopyProperties (Copy);
370 // Copy DatSet's tables
371 foreach (DataTable Table in Tables)
372 Copy.Tables.Add (Table.Copy ());
374 //Copy Relationships between tables after existance of tables
375 //and setting properties correctly
376 CopyRelations (Copy);
381 private void CopyProperties (DataSet Copy)
383 Copy.CaseSensitive = CaseSensitive;
384 //Copy.Container = Container
385 Copy.DataSetName = DataSetName;
386 //Copy.DefaultViewManager
388 Copy.EnforceConstraints = EnforceConstraints;
389 if(ExtendedProperties.Count > 0) {
390 // Cannot copy extended properties directly as the property does not have a set accessor
391 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
392 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
393 for (int i=0; i < ExtendedProperties.Count; i++)
394 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
396 Copy.Locale = Locale;
397 Copy.Namespace = Namespace;
398 Copy.Prefix = Prefix;
399 //Copy.Site = Site; // FIXME : Not sure of this.
404 private void CopyRelations (DataSet Copy)
407 //Creation of the relation contains some of the properties, and the constructor
408 //demands these values. instead changing the DataRelation constructor and behaviour the
409 //parameters are pre-configured and sent to the most general constructor
411 foreach (DataRelation MyRelation in this.Relations) {
412 string pTable = MyRelation.ParentTable.TableName;
413 string cTable = MyRelation.ChildTable.TableName;
414 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
415 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
418 foreach (DataColumn DC in MyRelation.ParentColumns) {
419 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
425 foreach (DataColumn DC in MyRelation.ChildColumns) {
426 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
430 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
431 //cRel.ChildColumns = MyRelation.ChildColumns;
432 //cRel.ChildTable = MyRelation.ChildTable;
433 //cRel.ExtendedProperties = cRel.ExtendedProperties;
434 //cRel.Nested = MyRelation.Nested;
435 //cRel.ParentColumns = MyRelation.ParentColumns;
436 //cRel.ParentTable = MyRelation.ParentTable;
438 Copy.Relations.Add (cRel);
445 public DataSet GetChanges ()
447 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
451 public DataSet GetChanges (DataRowState rowStates)
453 if (!HasChanges (rowStates))
456 DataSet copySet = Clone ();
457 Hashtable addedRows = new Hashtable ();
459 IEnumerator tableEnumerator = Tables.GetEnumerator ();
462 while (tableEnumerator.MoveNext ()) {
463 origTable = (DataTable)tableEnumerator.Current;
464 copyTable = copySet.Tables[origTable.TableName];
466 // Look for relations that have this table as child
467 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
469 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
470 while (rowEnumerator.MoveNext ()) {
471 DataRow row = (DataRow)rowEnumerator.Current;
473 if (row.IsRowChanged (rowStates))
474 AddChangedRow (addedRows, copySet, copyTable, relations, row);
480 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
482 if (addedRows.ContainsKey (row)) return;
485 while (relations.MoveNext ()) {
486 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
487 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
488 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
489 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
492 DataRow newRow = copyTable.NewRow ();
493 copyTable.Rows.Add (newRow);
494 row.CopyValuesToRow (newRow);
495 newRow.XmlRowID = row.XmlRowID;
496 addedRows.Add (row,row);
501 public DataTableReader GetDataReader (DataTable[] dataTables)
503 throw new NotImplementedException ();
507 public DataTableReader GetDataReader ()
509 throw new NotImplementedException ();
513 public string GetXml ()
515 StringWriter Writer = new StringWriter ();
516 WriteXml (Writer, XmlWriteMode.IgnoreSchema);
517 return Writer.ToString ();
520 public string GetXmlSchema ()
522 StringWriter Writer = new StringWriter ();
523 WriteXmlSchema (Writer);
524 return Writer.ToString ();
528 public bool HasChanges ()
530 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
534 public bool HasChanges (DataRowState rowState)
536 if (((int)rowState & 0xffffffe0) != 0)
537 throw new ArgumentOutOfRangeException ("rowState");
539 DataTableCollection tableCollection = Tables;
541 DataRowCollection rowCollection;
544 for (int i = 0; i < tableCollection.Count; i++) {
545 table = tableCollection[i];
546 rowCollection = table.Rows;
547 for (int j = 0; j < rowCollection.Count; j++) {
548 row = rowCollection[j];
549 if ((row.RowState & rowState) != 0)
557 public void InferXmlSchema (XmlReader reader, string[] nsArray)
561 XmlDocument doc = new XmlDocument ();
563 InferXmlSchema (doc, nsArray);
566 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
568 XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
571 public void InferXmlSchema (Stream stream, string[] nsArray)
573 InferXmlSchema (new XmlTextReader (stream), nsArray);
576 public void InferXmlSchema (TextReader reader, string[] nsArray)
578 InferXmlSchema (new XmlTextReader (reader), nsArray);
581 public void InferXmlSchema (string fileName, string[] nsArray)
583 XmlTextReader reader = new XmlTextReader (fileName);
585 InferXmlSchema (reader, nsArray);
593 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
595 throw new NotImplementedException ();
599 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
601 throw new NotImplementedException ();
605 public virtual void RejectChanges ()
608 bool oldEnforceConstraints = this.EnforceConstraints;
609 this.EnforceConstraints = false;
611 for (i = 0; i < this.Tables.Count;i++)
612 this.Tables[i].RejectChanges ();
614 this.EnforceConstraints = oldEnforceConstraints;
617 public virtual void Reset ()
619 IEnumerator constraintEnumerator;
621 // first we remove all ForeignKeyConstraints (if we will not do that
622 // we will get an exception when clearing the tables).
623 for (int i = 0; i < Tables.Count; i++) {
624 ConstraintCollection cc = Tables[i].Constraints;
625 for (int j = 0; j < cc.Count; j++) {
626 if (cc[j] is ForeignKeyConstraint)
636 public void WriteXml (Stream stream)
638 XmlTextWriter writer = new XmlTextWriter (stream, null);
639 writer.Formatting = Formatting.Indented;
644 /// Writes the current data for the DataSet to the specified file.
646 /// <param name="filename">Fully qualified filename to write to</param>
647 public void WriteXml (string fileName)
649 XmlTextWriter writer = new XmlTextWriter (fileName, null);
650 writer.Formatting = Formatting.Indented;
651 writer.WriteStartDocument (true);
656 writer.WriteEndDocument ();
661 public void WriteXml (TextWriter writer)
663 XmlTextWriter xwriter = new XmlTextWriter (writer);
664 xwriter.Formatting = Formatting.Indented;
668 public void WriteXml (XmlWriter writer)
670 WriteXml (writer, XmlWriteMode.IgnoreSchema);
673 public void WriteXml (string filename, XmlWriteMode mode)
675 XmlTextWriter writer = new XmlTextWriter (filename, null);
676 writer.Formatting = Formatting.Indented;
677 writer.WriteStartDocument (true);
680 WriteXml (writer, mode);
683 writer.WriteEndDocument ();
688 public void WriteXml (Stream stream, XmlWriteMode mode)
690 XmlTextWriter writer = new XmlTextWriter (stream, null);
691 writer.Formatting = Formatting.Indented;
692 WriteXml (writer, mode);
695 public void WriteXml (TextWriter writer, XmlWriteMode mode)
697 XmlTextWriter xwriter = new XmlTextWriter (writer);
698 xwriter.Formatting = Formatting.Indented;
699 WriteXml (xwriter, mode);
702 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
704 if (mode == XmlWriteMode.DiffGram) {
706 WriteDiffGramElement(writer);
709 // It should not write when there is no content to be written
710 bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
711 for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
712 shouldOutputContent = tableCollection [n].Rows.Count > 0;
714 if (shouldOutputContent) {
715 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
717 if (mode == XmlWriteMode.WriteSchema)
718 DoWriteXmlSchema (writer);
720 WriteTables (writer, mode, Tables, DataRowVersion.Default);
721 writer.WriteEndElement ();
724 if (mode == XmlWriteMode.DiffGram) {
725 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
727 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
728 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
729 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
730 writer.WriteEndElement ();
734 if (mode == XmlWriteMode.DiffGram)
735 writer.WriteEndElement (); // diffgr:diffgram
740 public void WriteXmlSchema (Stream stream)
742 XmlTextWriter writer = new XmlTextWriter (stream, null );
743 writer.Formatting = Formatting.Indented;
744 WriteXmlSchema (writer);
747 public void WriteXmlSchema (string fileName)
749 XmlTextWriter writer = new XmlTextWriter (fileName, null);
751 writer.Formatting = Formatting.Indented;
752 writer.WriteStartDocument (true);
753 WriteXmlSchema (writer);
755 writer.WriteEndDocument ();
760 public void WriteXmlSchema (TextWriter writer)
762 XmlTextWriter xwriter = new XmlTextWriter (writer);
764 xwriter.Formatting = Formatting.Indented;
765 // xwriter.WriteStartDocument ();
766 WriteXmlSchema (xwriter);
768 // xwriter.WriteEndDocument ();
773 public void WriteXmlSchema (XmlWriter writer)
775 //Create a skeleton doc and then write the schema
776 //proper which is common to the WriteXml method in schema mode
777 DoWriteXmlSchema (writer);
780 public void ReadXmlSchema (Stream stream)
782 XmlReader reader = new XmlTextReader (stream, null);
783 ReadXmlSchema (reader);
786 public void ReadXmlSchema (string str)
788 XmlReader reader = new XmlTextReader (str);
790 ReadXmlSchema (reader);
797 public void ReadXmlSchema (TextReader treader)
799 XmlReader reader = new XmlTextReader (treader);
800 ReadXmlSchema (reader);
803 public void ReadXmlSchema (XmlReader reader)
806 new XmlSchemaDataImporter (this, reader).Process ();
808 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
809 SchemaMapper.Read (reader);
813 public XmlReadMode ReadXml (Stream stream)
815 return ReadXml (new XmlTextReader (stream));
818 public XmlReadMode ReadXml (string str)
820 XmlTextReader reader = new XmlTextReader (str);
822 return ReadXml (reader);
829 public XmlReadMode ReadXml (TextReader reader)
831 return ReadXml (new XmlTextReader (reader));
834 public XmlReadMode ReadXml (XmlReader r)
836 return ReadXml (r, XmlReadMode.Auto);
839 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
841 return ReadXml (new XmlTextReader (stream), mode);
844 public XmlReadMode ReadXml (string str, XmlReadMode mode)
846 XmlTextReader reader = new XmlTextReader (str);
848 return ReadXml (reader, mode);
855 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
857 return ReadXml (new XmlTextReader (reader), mode);
860 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
862 switch (reader.ReadState) {
863 case ReadState.EndOfFile:
864 case ReadState.Error:
865 case ReadState.Closed:
868 // Skip XML declaration and prolog
869 reader.MoveToContent();
873 XmlReadMode Result = mode;
875 // If diffgram, then read the first element as diffgram
876 if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
878 case XmlReadMode.Auto:
879 case XmlReadMode.DiffGram:
880 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
881 DiffLoader.Load (reader);
882 // (and leave rest of the reader as is)
883 return XmlReadMode.DiffGram;
884 case XmlReadMode.Fragment:
886 // (and continue to read)
890 // (and leave rest of the reader as is)
894 // If schema, then read the first element as schema
895 if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
897 case XmlReadMode.IgnoreSchema:
898 case XmlReadMode.InferSchema:
900 // (and break up read)
902 case XmlReadMode.Fragment:
903 ReadXmlSchema (reader);
904 // (and continue to read)
906 case XmlReadMode.Auto:
907 if (Tables.Count == 0) {
908 ReadXmlSchema (reader);
909 return XmlReadMode.ReadSchema;
911 // otherwise just ignore and return IgnoreSchema
913 return XmlReadMode.IgnoreSchema;
916 ReadXmlSchema (reader);
917 // (and leave rest of the reader as is)
918 return mode; // When DiffGram, return DiffGram
921 // Otherwise, read as dataset... but only when required.
922 XmlReadMode explicitReturnMode = XmlReadMode.Auto;
925 case XmlReadMode.Auto:
926 if (Tables.Count > 0)
927 goto case XmlReadMode.IgnoreSchema;
929 goto case XmlReadMode.InferSchema;
930 case XmlReadMode.InferSchema:
931 doc = new XmlDocument ();
933 doc.AppendChild (doc.ReadNode (reader));
934 reader.MoveToContent ();
935 if (doc.DocumentElement != null)
937 } while (!reader.EOF);
938 InferXmlSchema (doc, null);
939 reader = new XmlNodeReader (doc);
940 explicitReturnMode = XmlReadMode.InferSchema;
942 case XmlReadMode.ReadSchema:
943 doc = new XmlDocument ();
945 doc.AppendChild (doc.ReadNode (reader));
946 reader.MoveToContent ();
947 if (doc.DocumentElement != null)
949 } while (!reader.EOF);
950 if (doc.DocumentElement != null) {
951 XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
952 if (schema != null) {
953 ReadXmlSchema (new XmlNodeReader (schema));
954 explicitReturnMode = XmlReadMode.ReadSchema;
957 reader = new XmlNodeReader (doc);
959 case XmlReadMode.IgnoreSchema:
960 case XmlReadMode.Fragment:
967 XmlDataReader.ReadXml (this, reader, mode);
968 if (explicitReturnMode != XmlReadMode.Auto)
969 return explicitReturnMode;
970 return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
972 #endregion // Public Methods
974 #region Public Events
976 [DataCategory ("Action")]
977 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
978 public event MergeFailedEventHandler MergeFailed;
980 #endregion // Public Events
982 #region IListSource methods
983 IList IListSource.GetList ()
985 return DefaultViewManager;
988 bool IListSource.ContainsListCollection {
993 #endregion IListSource methods
995 #region ISupportInitialize methods
996 public void BeginInit ()
1000 public void EndInit ()
1005 #region ISerializable
1006 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1008 StringWriter sw = new StringWriter ();
1009 XmlTextWriter writer = new XmlTextWriter (sw);
1010 DoWriteXmlSchema (writer);
1012 si.AddValue ("XmlSchema", sw.ToString ());
1014 sw = new StringWriter ();
1015 writer = new XmlTextWriter (sw);
1016 WriteXml (writer, XmlWriteMode.DiffGram);
1018 si.AddValue ("XmlDiffGram", sw.ToString ());
1022 #region Protected Methods
1023 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1025 string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1026 XmlTextReader reader = new XmlTextReader (new StringReader (s));
1027 ReadXmlSchema (reader);
1030 s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1031 reader = new XmlTextReader (new StringReader (s));
1032 ReadXml (reader, XmlReadMode.DiffGram);
1037 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1042 protected virtual void ReadXmlSerializable (XmlReader reader)
1044 reader.MoveToContent ();
1045 reader.ReadStartElement ();
1046 reader.MoveToContent ();
1047 ReadXmlSchema (reader);
1048 reader.MoveToContent ();
1049 ReadXml (reader, XmlReadMode.DiffGram);
1050 reader.MoveToContent ();
1051 reader.ReadEndElement ();
1054 void IXmlSerializable.ReadXml (XmlReader reader)
1056 ReadXmlSerializable(reader);
1059 void IXmlSerializable.WriteXml (XmlWriter writer)
1061 DoWriteXmlSchema (writer);
1062 WriteXml (writer, XmlWriteMode.DiffGram);
1065 XmlSchema IXmlSerializable.GetSchema ()
1067 return BuildSchema ();
1070 protected virtual bool ShouldSerializeRelations ()
1075 protected virtual bool ShouldSerializeTables ()
1081 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1086 protected virtual void OnRemoveRelation (DataRelation relation)
1091 protected virtual void OnRemoveTable (DataTable table)
1095 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1097 if (MergeFailed != null)
1098 MergeFailed (this, e);
1102 protected internal void RaisePropertyChanging (string name)
1107 #region Private Xml Serialisation
1109 private string WriteObjectXml (object o)
1111 switch (Type.GetTypeCode (o.GetType ())) {
1112 case TypeCode.Boolean:
1113 return XmlConvert.ToString ((Boolean) o);
1115 return XmlConvert.ToString ((Byte) o);
1117 return XmlConvert.ToString ((Char) o);
1118 case TypeCode.DateTime:
1119 return XmlConvert.ToString ((DateTime) o);
1120 case TypeCode.Decimal:
1121 return XmlConvert.ToString ((Decimal) o);
1122 case TypeCode.Double:
1123 return XmlConvert.ToString ((Double) o);
1124 case TypeCode.Int16:
1125 return XmlConvert.ToString ((Int16) o);
1126 case TypeCode.Int32:
1127 return XmlConvert.ToString ((Int32) o);
1128 case TypeCode.Int64:
1129 return XmlConvert.ToString ((Int64) o);
1130 case TypeCode.SByte:
1131 return XmlConvert.ToString ((SByte) o);
1132 case TypeCode.Single:
1133 return XmlConvert.ToString ((Single) o);
1134 case TypeCode.UInt16:
1135 return XmlConvert.ToString ((UInt16) o);
1136 case TypeCode.UInt32:
1137 return XmlConvert.ToString ((UInt32) o);
1138 case TypeCode.UInt64:
1139 return XmlConvert.ToString ((UInt64) o);
1141 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1142 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1143 if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1144 return o.ToString ();
1147 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1149 //Write out each table in order, providing it is not
1150 //part of another table structure via a nested parent relationship
1151 foreach (DataTable table in tableCollection) {
1152 bool isTopLevel = true;
1154 foreach (DataRelation rel in table.ParentRelations) {
1162 WriteTable ( writer, table, mode, version);
1167 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1169 DataRow[] rows = new DataRow [table.Rows.Count];
1170 table.Rows.CopyTo (rows, 0);
1171 WriteTable (writer, rows, mode, version, true);
1174 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
1176 //The columns can be attributes, hidden, elements, or simple content
1177 //There can be 0-1 simple content cols or 0-* elements
1178 System.Collections.ArrayList atts;
1179 System.Collections.ArrayList elements;
1180 DataColumn simple = null;
1182 if (rows.Length == 0) return;
1183 DataTable table = rows[0].Table;
1184 SplitColumns (table, out atts, out elements, out simple);
1185 //sort out the namespacing
1186 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1187 int relationCount = table.ParentRelations.Count;
1188 DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1190 foreach (DataRow row in rows) {
1192 // Skip rows that is a child of any tables.
1193 switch (relationCount) {
1199 if (row.GetParentRow (oneRel) != null)
1204 for (int i = 0; i < table.ParentRelations.Count; i++) {
1205 DataRelation prel = table.ParentRelations [i];
1208 if (row.GetParentRow (prel) != null) {
1219 if (!row.HasVersion(version) ||
1220 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1221 && version == DataRowVersion.Original))
1224 // First check are all the rows null. If they are we just write empty element
1225 bool AllNulls = true;
1226 foreach (DataColumn dc in table.Columns) {
1228 if (row [dc.ColumnName, version] != DBNull.Value) {
1234 // If all of the columns were null, we have to write empty element
1236 writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1240 WriteTableElement (writer, mode, table, row, version);
1242 foreach (DataColumn col in atts) {
1243 WriteColumnAsAttribute (writer, mode, col, row, version);
1246 if (simple != null) {
1247 writer.WriteString (WriteObjectXml (row[simple, version]));
1250 foreach (DataColumn col in elements) {
1251 WriteColumnAsElement (writer, mode, col, row, version);
1255 foreach (DataRelation relation in table.ChildRelations) {
1256 if (relation.Nested) {
1257 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1261 writer.WriteEndElement ();
1266 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1268 string colnspc = null;
1269 object rowObject = row [col, version];
1271 if (rowObject == null || rowObject == DBNull.Value)
1274 if (col.Namespace != String.Empty)
1275 colnspc = col.Namespace;
1277 //TODO check if I can get away with write element string
1278 WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1279 writer.WriteString (WriteObjectXml (rowObject));
1280 writer.WriteEndElement ();
1283 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1285 if (!row.IsNull (col))
1286 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1289 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1291 //sort out the namespacing
1292 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1294 WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1296 if (mode == XmlWriteMode.DiffGram) {
1297 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1298 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1299 string modeName = null;
1300 if (row.RowState == DataRowState.Modified)
1301 modeName = "modified";
1302 else if (row.RowState == DataRowState.Added)
1303 modeName = "inserted";
1305 if (version != DataRowVersion.Original && modeName != null)
1306 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1310 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1312 writer.WriteStartElement (prefix, name, nspc);
1315 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1318 case XmlWriteMode.WriteSchema:
1319 writer.WriteAttributeString (prefix, name, nspc);
1321 case XmlWriteMode.DiffGram:
1322 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1325 writer.WriteAttributeString (name, stringValue);
1330 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1332 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1334 if (mode == XmlWriteMode.DiffGram) {
1335 SetTableRowsID (table);
1336 WriteDiffGramElement (writer);
1339 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1341 WriteTable (writer, table, mode, DataRowVersion.Default);
1343 if (mode == XmlWriteMode.DiffGram) {
1344 writer.WriteEndElement (); //DataSet name
1345 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1347 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1348 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1349 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1350 writer.WriteEndElement ();
1353 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1357 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1359 if (ns == String.Empty)
1361 if (ns != nsmgr.DefaultNamespace) {
1362 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1363 for (int i = 1; i < int.MaxValue; i++) {
1364 string p = nsmgr.NameTable.Add ("app" + i);
1365 if (!nsmgr.HasNamespace (p)) {
1366 nsmgr.AddNamespace (p, ns);
1367 HandleExternalNamespace (p, ns, schema);
1375 XmlSchema BuildSchema ()
1377 return BuildSchema (Tables, Relations);
1380 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1382 string constraintPrefix = "";
1383 XmlSchema schema = new XmlSchema ();
1384 XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1386 if (Namespace != "") {
1387 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1388 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1389 schema.TargetNamespace = Namespace;
1390 constraintPrefix = XmlConstants.TnsPrefix + ":";
1393 // set the schema id
1394 string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1395 schema.Id = DataSetName;
1396 XmlDocument doc = new XmlDocument ();
1397 XmlAttribute attr = null;
1398 ArrayList atts = new ArrayList ();
1400 attr = doc.CreateAttribute ("", "xmlns", xmlNSURI);
1403 nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1404 nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1405 if (Namespace != "") {
1406 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1407 nsmgr.AddNamespace (String.Empty, Namespace);
1409 if (CheckExtendedPropertyExists ())
1410 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1413 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1415 XmlSchemaElement elem = new XmlSchemaElement ();
1416 elem.Name = XmlConvert.EncodeName (DataSetName);
1418 // Add namespaces used in DataSet components (tables, columns, ...)
1419 foreach (DataTable dt in Tables) {
1420 foreach (DataColumn col in dt.Columns)
1421 CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1422 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1425 // Attributes for DataSet element
1427 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1428 attr.Value = "true";
1431 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1432 attr.Value = locale.Name;
1435 elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1437 AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1439 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1440 elem.SchemaType = complex;
1442 XmlSchemaChoice choice = new XmlSchemaChoice ();
1443 complex.Particle = choice;
1444 choice.MaxOccursString = XmlConstants.Unbounded;
1446 //Write out schema for each table in order
1447 foreach (DataTable table in tables) {
1448 bool isTopLevel = true;
1449 foreach (DataRelation rel in table.ParentRelations) {
1457 if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1458 XmlSchemaElement extElem = new XmlSchemaElement ();
1459 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1460 choice.Items.Add (extElem);
1463 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1467 schema.Items.Add (elem);
1469 AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1470 foreach (string prefix in nsmgr) {
1471 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1472 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1473 schema.Namespaces.Add (prefix, ns);
1478 private bool CheckExtendedPropertyExists ()
1480 if (ExtendedProperties.Count > 0)
1482 foreach (DataTable dt in Tables) {
1483 if (dt.ExtendedProperties.Count > 0)
1485 foreach (DataColumn col in dt.Columns)
1486 if (col.ExtendedProperties.Count > 0)
1488 foreach (Constraint c in dt.Constraints)
1489 if (c.ExtendedProperties.Count > 0)
1492 foreach (DataRelation rel in Relations)
1493 if (rel.ExtendedProperties.Count > 0)
1498 // Add all constraints in all tables to the schema.
1499 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
1501 // first add all unique constraints.
1502 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1503 // Add all foriegn key constraints.
1504 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1507 // Add unique constaraints to the schema.
1508 // return hashtable with the names of all XmlSchemaUnique elements we created.
1509 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1511 Hashtable uniqueNames = new Hashtable();
1512 foreach (DataTable table in tables) {
1514 foreach (Constraint constraint in table.Constraints) {
1516 if (constraint is UniqueConstraint) {
1517 ArrayList attrs = new ArrayList ();
1518 XmlAttribute attrib;
1519 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1520 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1522 // if column of the constraint is hidden do not write the constraint.
1523 bool isHidden = false;
1524 foreach (DataColumn column in uqConst.Columns) {
1525 if (column.ColumnMapping == MappingType.Hidden) {
1534 // if constaraint name do not exist in the hashtable we can use it.
1535 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1536 uniq.Name = uqConst.ConstraintName;
1538 // generate new constraint name for the XmlSchemaUnique element.
1540 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1541 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1542 attrib.Value = uqConst.ConstraintName;
1545 if (uqConst.IsPrimaryKey) {
1546 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1547 attrib.Value = "true";
1551 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1553 uniq.Selector = new XmlSchemaXPath();
1554 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1555 XmlSchemaXPath field;
1556 foreach (DataColumn column in uqConst.Columns) {
1557 field = new XmlSchemaXPath();
1558 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1559 field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1560 uniq.Fields.Add(field);
1563 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1565 elem.Constraints.Add (uniq);
1566 uniqueNames.Add (uniq.Name, null);
1573 // Add the foriegn keys to the schema.
1574 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1576 if (relations == null) return;
1578 foreach (DataRelation rel in relations) {
1580 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1583 bool isHidden = false;
1584 foreach (DataColumn col in rel.ParentColumns) {
1585 if (col.ColumnMapping == MappingType.Hidden) {
1590 foreach (DataColumn col in rel.ChildColumns) {
1591 if (col.ColumnMapping == MappingType.Hidden) {
1599 ArrayList attrs = new ArrayList ();
1600 XmlAttribute attrib;
1601 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1602 keyRef.Name = rel.RelationName;
1603 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1604 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1606 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1607 // first try to find the concatenated name. If we didn't find it - use constraint name.
1608 if (uniqueNames.ContainsKey (concatName)) {
1609 keyRef.Refer = new XmlQualifiedName(concatName);
1612 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1616 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1617 attrib.Value = "true";
1621 keyRef.Selector = new XmlSchemaXPath();
1622 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1623 XmlSchemaXPath field;
1624 foreach (DataColumn column in rel.ChildColumns) {
1625 field = new XmlSchemaXPath();
1626 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1627 field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1628 keyRef.Fields.Add(field);
1631 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1632 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1634 elem.Constraints.Add (keyRef);
1638 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1644 ArrayList xattrs = new ArrayList();
1647 SplitColumns (table, out atts, out elements, out simple);
1649 XmlSchemaElement elem = new XmlSchemaElement ();
1650 elem.Name = table.TableName;
1652 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1653 elem.SchemaType = complex;
1655 XmlSchemaObjectCollection schemaAttributes = null;
1657 if (simple != null) {
1658 // add simpleContent
1659 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1660 complex.ContentModel = simpleContent;
1662 // add column name attribute
1663 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1664 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1665 xlmAttrs[0].Value = simple.ColumnName;
1667 // add ordinal attribute
1668 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1669 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1670 simpleContent.UnhandledAttributes = xlmAttrs;
1674 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1675 simpleContent.Content = extension;
1676 extension.BaseTypeName = MapType (simple.DataType);
1677 schemaAttributes = extension.Attributes;
1679 schemaAttributes = complex.Attributes;
1680 //A sequence of element types or a simple content node
1682 XmlSchemaSequence seq = new XmlSchemaSequence ();
1684 foreach (DataColumn col in elements) {
1686 // Add element for the column.
1687 XmlSchemaElement colElem = new XmlSchemaElement ();
1688 colElem.Name = col.ColumnName;
1690 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1691 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1692 xattr.Value = col.Caption;
1696 if (col.AutoIncrement == true) {
1697 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1698 xattr.Value = "true";
1702 if (col.AutoIncrementSeed != 0) {
1703 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1704 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1708 if (col.DefaultValue.ToString () != String.Empty)
1709 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1712 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1713 xattr.Value = "true";
1717 if (col.MaxLength < 0)
1718 colElem.SchemaTypeName = MapType (col.DataType);
1720 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1721 && col.DataType != typeof (char)) {
1722 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1723 xattr.Value = col.DataType.AssemblyQualifiedName;
1727 if (col.AllowDBNull) {
1728 colElem.MinOccurs = 0;
1731 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1732 // XmlConstants.Ordinal,
1733 // XmlConstants.MsdataNamespace,
1734 // col.Ordinal.ToString ());
1736 // Write SimpleType if column have MaxLength
1737 if (col.MaxLength > -1) {
1738 colElem.SchemaType = GetTableSimpleType (doc, col);
1741 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1743 AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1744 seq.Items.Add (colElem);
1747 foreach (DataRelation rel in table.ChildRelations) {
1749 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1750 XmlSchemaElement el = new XmlSchemaElement ();
1751 el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1753 XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1755 el.MaxOccursString = "unbounded";
1756 XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1758 el.SchemaType = null;
1759 el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1760 schemaToAdd.Items.Add (ct);
1766 if (seq.Items.Count > 0)
1767 complex.Particle = seq;
1770 //Then a list of attributes
1771 foreach (DataColumn col in atts) {
1772 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1773 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1774 att.Name = col.ColumnName;
1775 if (col.Namespace != String.Empty) {
1776 att.Form = XmlSchemaForm.Qualified;
1777 string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1778 att.Name = prefix + ":" + col.ColumnName;
1779 // FIXME: Handle prefix mapping correctly.
1780 schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1782 if (!col.AllowDBNull)
1783 att.Use = XmlSchemaUse.Required;
1785 if (col.MaxLength > -1)
1786 att.SchemaType = GetTableSimpleType (doc, col);
1788 att.SchemaTypeName = MapType (col.DataType);
1789 // FIXME: what happens if extended properties are set on attribute columns??
1790 if (!col.AllowDBNull)
1791 att.Use = XmlSchemaUse.Required;
1792 if (col.DefaultValue.ToString () != String.Empty)
1793 att.DefaultValue = WriteObjectXml (col.DefaultValue);
1796 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1797 xattr.Value = "true";
1801 att.UnhandledAttributes = xattrs.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1804 if (col.MaxLength > -1)
1805 att.SchemaType = GetTableSimpleType (doc, col);
1807 att.SchemaTypeName = MapType (col.DataType);
1808 schemaAttributes.Add (att);
1811 AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1816 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1818 ArrayList attList = new ArrayList ();
1819 XmlAttribute xmlAttr;
1821 if (xsobj.UnhandledAttributes != null)
1822 attList.AddRange (xsobj.UnhandledAttributes);
1824 // add extended properties to xs:element
1825 foreach (DictionaryEntry de in props) {
1826 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1827 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1828 attList.Add (xmlAttr);
1830 if (attList.Count > 0)
1831 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1834 private string SafeNS (string ns)
1836 return ns != null ? ns : String.Empty;
1839 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1841 foreach (XmlSchemaExternal ext in schema.Includes) {
1842 XmlSchemaImport imp = ext as XmlSchemaImport;
1843 if (imp != null && imp.Namespace == ns)
1844 return; // nothing to do
1846 XmlSchemaImport i = new XmlSchemaImport ();
1848 i.SchemaLocation = "_" + prefix + ".xsd";
1849 schema.Includes.Add (i);
1852 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1855 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1858 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1859 restriction.BaseTypeName = MapType (col.DataType);
1862 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1863 max.Value = XmlConvert.ToString (col.MaxLength);
1864 restriction.Facets.Add (max);
1866 simple.Content = restriction;
1870 private void DoWriteXmlSchema (XmlWriter writer)
1872 BuildSchema ().Write (writer);
1876 /// Helper function to split columns into attributes elements and simple
1879 private void SplitColumns (DataTable table,
1881 out ArrayList elements,
1882 out DataColumn simple)
1884 //The columns can be attributes, hidden, elements, or simple content
1885 //There can be 0-1 simple content cols or 0-* elements
1886 atts = new System.Collections.ArrayList ();
1887 elements = new System.Collections.ArrayList ();
1890 //Sort out the columns
1891 foreach (DataColumn col in table.Columns) {
1892 switch (col.ColumnMapping) {
1893 case MappingType.Attribute:
1896 case MappingType.Element:
1899 case MappingType.SimpleContent:
1900 if (simple != null) {
1901 throw new System.InvalidOperationException ("There may only be one simple content element");
1906 //ignore Hidden elements
1912 private void WriteDiffGramElement(XmlWriter writer)
1914 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1915 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1918 private void SetRowsID()
1920 foreach (DataTable Table in Tables)
1921 SetTableRowsID (Table);
1924 private void SetTableRowsID (DataTable Table)
1927 foreach (DataRow Row in Table.Rows) {
1928 Row.XmlRowID = dataRowID;
1934 private XmlQualifiedName MapType (Type type)
1936 switch (Type.GetTypeCode (type)) {
1937 case TypeCode.String: return XmlConstants.QnString;
1938 case TypeCode.Int16: return XmlConstants.QnShort;
1939 case TypeCode.Int32: return XmlConstants.QnInt;
1940 case TypeCode.Int64: return XmlConstants.QnLong;
1941 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1942 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1943 //case TypeCode.Char: return XmlConstants.QnChar;
1944 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1945 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1946 case TypeCode.Double: return XmlConstants.QnDouble;
1947 case TypeCode.SByte: return XmlConstants.QnSbyte;
1948 case TypeCode.Single: return XmlConstants.QnFloat;
1949 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1950 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1951 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1954 if (typeof (TimeSpan) == type)
1955 return XmlConstants.QnDuration;
1956 else if (typeof (System.Uri) == type)
1957 return XmlConstants.QnUri;
1958 else if (typeof (byte[]) == type)
1959 return XmlConstants.QnBase64Binary;
1960 else if (typeof (XmlQualifiedName) == type)
1961 return XmlConstants.QnXmlQualifiedName;
1963 return XmlConstants.QnString;
1966 #endregion //Private Xml Serialisation