2 // System.Data/DataSet.cs
5 // Christopher Podurgiel <cpodurgiel@msn.com>
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Rodrigo Moya <rodrigo@ximian.com>
8 // Stuart Caborn <stuart.caborn@virgin.net>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
11 // Atsushi Enomoto <atsushi@ximian.com>
13 // (C) Ximian, Inc. 2002
14 // Copyright (C) Tim Coleman, 2002, 2003
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 using System.Collections;
42 using System.ComponentModel;
43 using System.Globalization;
44 using System.Threading;
46 using System.Runtime.Serialization;
48 using System.Xml.Schema;
49 using System.Xml.Serialization;
50 using System.Data.Common;
52 namespace System.Data {
55 [DefaultProperty ("DataSetName")]
56 [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataSetDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
59 public class DataSet : MarshalByValueComponent, IListSource,
60 ISupportInitialize, ISerializable, IXmlSerializable
62 private string dataSetName;
63 private string _namespace = "";
64 private string prefix;
65 private bool caseSensitive;
66 private bool enforceConstraints = true;
67 private DataTableCollection tableCollection;
68 private DataRelationCollection relationCollection;
69 private PropertyCollection properties;
70 private DataViewManager defaultView;
71 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
72 internal XmlDataDocument _xmlDataDocument = null;
76 public DataSet () : this ("NewDataSet")
80 public DataSet (string name)
83 tableCollection = new DataTableCollection (this);
84 relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
85 properties = new PropertyCollection ();
86 this.prefix = String.Empty;
88 this.Locale = CultureInfo.CurrentCulture;
91 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
93 GetSerializationData (info, context);
96 #endregion // Constructors
98 #region Public Properties
100 [DataCategory ("Data")]
101 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
102 [DefaultValue (false)]
103 public bool CaseSensitive {
105 return caseSensitive;
108 caseSensitive = value;
109 if (!caseSensitive) {
110 foreach (DataTable table in Tables) {
111 foreach (Constraint c in table.Constraints)
112 c.AssertConstraint ();
118 [DataCategory ("Data")]
119 [DataSysDescription ("The name of this DataSet.")]
121 public string DataSetName {
122 get { return dataSetName; }
123 set { dataSetName = value; }
126 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
128 public DataViewManager DefaultViewManager {
130 if (defaultView == null)
131 defaultView = new DataViewManager (this);
136 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
137 [DefaultValue (true)]
138 public bool EnforceConstraints {
139 get { return enforceConstraints; }
141 if (value != enforceConstraints) {
142 enforceConstraints = value;
144 foreach (DataTable table in Tables) {
145 // first assert all unique constraints
146 foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
147 uc.AssertConstraint ();
148 // then assert all foreign keys
149 foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
150 fk.AssertConstraint ();
158 [DataCategory ("Data")]
159 [DataSysDescription ("The collection that holds custom user information.")]
160 public PropertyCollection ExtendedProperties {
161 get { return properties; }
165 [DataSysDescription ("Indicates that the DataSet has errors.")]
166 public bool HasErrors {
169 for (int i = 0; i < Tables.Count; i++) {
170 if (Tables[i].HasErrors)
177 [DataCategory ("Data")]
178 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
179 public CultureInfo Locale {
184 if (locale == null || !locale.Equals (value)) {
185 // TODO: check if the new locale is valid
186 // TODO: update locale of all tables
192 public void Merge (DataRow[] rows)
194 Merge (rows, false, MissingSchemaAction.Add);
197 public void Merge (DataSet dataSet)
199 Merge (dataSet, false, MissingSchemaAction.Add);
202 public void Merge (DataTable table)
204 Merge (table, false, MissingSchemaAction.Add);
207 public void Merge (DataSet dataSet, bool preserveChanges)
209 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
213 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
216 throw new ArgumentNullException ("rows");
217 if (!IsLegalSchemaAction (missingSchemaAction))
218 throw new ArgumentOutOfRangeException ("missingSchemaAction");
220 MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
224 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
227 throw new ArgumentNullException ("dataSet");
228 if (!IsLegalSchemaAction (missingSchemaAction))
229 throw new ArgumentOutOfRangeException ("missingSchemaAction");
231 MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
235 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
238 throw new ArgumentNullException ("table");
239 if (!IsLegalSchemaAction (missingSchemaAction))
240 throw new ArgumentOutOfRangeException ("missingSchemaAction");
242 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
245 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
247 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
248 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
253 [DataCategory ("Data")]
254 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
256 public string Namespace {
257 get { return _namespace; }
259 //TODO - trigger an event if this happens?
261 value = String.Empty;
262 if (value != this._namespace)
263 RaisePropertyChanging ("Namespace");
268 [DataCategory ("Data")]
269 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
271 public string Prefix {
272 get { return prefix; }
275 value = String.Empty;
276 // Prefix cannot contain any special characters other than '_' and ':'
277 for (int i = 0; i < value.Length; i++) {
278 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
279 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
284 value = string.Empty;
286 if (value != this.prefix)
287 RaisePropertyChanging ("Prefix");
292 [DataCategory ("Data")]
293 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
294 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
295 public DataRelationCollection Relations {
297 return relationCollection;
302 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
303 public override ISite Site {
306 throw new NotImplementedException ();
311 throw new NotImplementedException ();
315 [DataCategory ("Data")]
316 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
318 public DataTableCollection Tables {
319 get { return tableCollection; }
322 #endregion // Public Properties
324 #region Public Methods
327 public void AcceptChanges ()
329 foreach (DataTable tempTable in tableCollection)
330 tempTable.AcceptChanges ();
334 /// Clears all the tables
338 if (_xmlDataDocument != null)
339 throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
340 bool enforceConstraints = this.EnforceConstraints;
341 this.EnforceConstraints = false;
342 for (int t = 0; t < tableCollection.Count; t++) {
343 tableCollection[t].Clear ();
345 this.EnforceConstraints = enforceConstraints;
348 public virtual DataSet Clone ()
350 // need to return the same type as this...
351 DataSet Copy = (DataSet) Activator.CreateInstance(GetType(), true);
353 CopyProperties (Copy);
355 foreach (DataTable Table in Tables) {
356 // tables are often added in no-args constructor, don't add them
358 if (!Copy.Tables.Contains(Table.TableName)) {
359 Copy.Tables.Add (Table.Clone ());
363 //Copy Relationships between tables after existance of tables
364 //and setting properties correctly
365 CopyRelations (Copy);
370 // Copies both the structure and data for this DataSet.
371 public DataSet Copy ()
373 DataSet Copy = new DataSet ();
374 CopyProperties (Copy);
376 // Copy DatSet's tables
377 foreach (DataTable Table in Tables)
378 Copy.Tables.Add (Table.Copy ());
380 //Copy Relationships between tables after existance of tables
381 //and setting properties correctly
382 CopyRelations (Copy);
387 private void CopyProperties (DataSet Copy)
389 Copy.CaseSensitive = CaseSensitive;
390 //Copy.Container = Container
391 Copy.DataSetName = DataSetName;
392 //Copy.DefaultViewManager
394 Copy.EnforceConstraints = EnforceConstraints;
395 if(ExtendedProperties.Count > 0) {
396 // Cannot copy extended properties directly as the property does not have a set accessor
397 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
398 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
399 for (int i=0; i < ExtendedProperties.Count; i++)
400 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
402 Copy.Locale = Locale;
403 Copy.Namespace = Namespace;
404 Copy.Prefix = Prefix;
405 //Copy.Site = Site; // FIXME : Not sure of this.
410 private void CopyRelations (DataSet Copy)
413 //Creation of the relation contains some of the properties, and the constructor
414 //demands these values. instead changing the DataRelation constructor and behaviour the
415 //parameters are pre-configured and sent to the most general constructor
417 foreach (DataRelation MyRelation in this.Relations) {
418 string pTable = MyRelation.ParentTable.TableName;
419 string cTable = MyRelation.ChildTable.TableName;
420 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
421 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
424 foreach (DataColumn DC in MyRelation.ParentColumns) {
425 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
431 foreach (DataColumn DC in MyRelation.ChildColumns) {
432 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
436 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
437 //cRel.ChildColumns = MyRelation.ChildColumns;
438 //cRel.ChildTable = MyRelation.ChildTable;
439 //cRel.ExtendedProperties = cRel.ExtendedProperties;
440 //cRel.Nested = MyRelation.Nested;
441 //cRel.ParentColumns = MyRelation.ParentColumns;
442 //cRel.ParentTable = MyRelation.ParentTable;
444 Copy.Relations.Add (cRel);
451 public DataSet GetChanges ()
453 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
457 public DataSet GetChanges (DataRowState rowStates)
459 if (!HasChanges (rowStates))
462 DataSet copySet = Clone ();
463 Hashtable addedRows = new Hashtable ();
465 IEnumerator tableEnumerator = Tables.GetEnumerator ();
468 while (tableEnumerator.MoveNext ()) {
469 origTable = (DataTable)tableEnumerator.Current;
470 copyTable = copySet.Tables[origTable.TableName];
472 // Look for relations that have this table as child
473 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
475 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
476 while (rowEnumerator.MoveNext ()) {
477 DataRow row = (DataRow)rowEnumerator.Current;
479 if (row.IsRowChanged (rowStates))
480 AddChangedRow (addedRows, copySet, copyTable, relations, row);
486 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
488 if (addedRows.ContainsKey (row)) return;
491 while (relations.MoveNext ()) {
492 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
493 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
494 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
495 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
498 DataRow newRow = copyTable.NewRow ();
499 copyTable.Rows.Add (newRow);
500 row.CopyValuesToRow (newRow);
501 newRow.XmlRowID = row.XmlRowID;
502 addedRows.Add (row,row);
507 public DataTableReader GetDataReader (DataTable[] dataTables)
509 throw new NotImplementedException ();
513 public DataTableReader GetDataReader ()
515 throw new NotImplementedException ();
519 public string GetXml ()
521 StringWriter Writer = new StringWriter ();
522 WriteXml (Writer, XmlWriteMode.IgnoreSchema);
523 return Writer.ToString ();
526 public string GetXmlSchema ()
528 StringWriter Writer = new StringWriter ();
529 WriteXmlSchema (Writer);
530 return Writer.ToString ();
534 public bool HasChanges ()
536 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
540 public bool HasChanges (DataRowState rowState)
542 if (((int)rowState & 0xffffffe0) != 0)
543 throw new ArgumentOutOfRangeException ("rowState");
545 DataTableCollection tableCollection = Tables;
547 DataRowCollection rowCollection;
550 for (int i = 0; i < tableCollection.Count; i++) {
551 table = tableCollection[i];
552 rowCollection = table.Rows;
553 for (int j = 0; j < rowCollection.Count; j++) {
554 row = rowCollection[j];
555 if ((row.RowState & rowState) != 0)
563 public void InferXmlSchema (XmlReader reader, string[] nsArray)
567 XmlDocument doc = new XmlDocument ();
569 InferXmlSchema (doc, nsArray);
572 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
574 XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
577 public void InferXmlSchema (Stream stream, string[] nsArray)
579 InferXmlSchema (new XmlTextReader (stream), nsArray);
582 public void InferXmlSchema (TextReader reader, string[] nsArray)
584 InferXmlSchema (new XmlTextReader (reader), nsArray);
587 public void InferXmlSchema (string fileName, string[] nsArray)
589 XmlTextReader reader = new XmlTextReader (fileName);
591 InferXmlSchema (reader, nsArray);
599 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
601 throw new NotImplementedException ();
605 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
607 throw new NotImplementedException ();
611 public virtual void RejectChanges ()
614 bool oldEnforceConstraints = this.EnforceConstraints;
615 this.EnforceConstraints = false;
617 for (i = 0; i < this.Tables.Count;i++)
618 this.Tables[i].RejectChanges ();
620 this.EnforceConstraints = oldEnforceConstraints;
623 public virtual void Reset ()
625 IEnumerator constraintEnumerator;
627 // first we remove all ForeignKeyConstraints (if we will not do that
628 // we will get an exception when clearing the tables).
629 for (int i = 0; i < Tables.Count; i++) {
630 ConstraintCollection cc = Tables[i].Constraints;
631 for (int j = 0; j < cc.Count; j++) {
632 if (cc[j] is ForeignKeyConstraint)
642 public void WriteXml (Stream stream)
644 XmlTextWriter writer = new XmlTextWriter (stream, null);
645 writer.Formatting = Formatting.Indented;
650 /// Writes the current data for the DataSet to the specified file.
652 /// <param name="filename">Fully qualified filename to write to</param>
653 public void WriteXml (string fileName)
655 XmlTextWriter writer = new XmlTextWriter (fileName, null);
656 writer.Formatting = Formatting.Indented;
657 writer.WriteStartDocument (true);
662 writer.WriteEndDocument ();
667 public void WriteXml (TextWriter writer)
669 XmlTextWriter xwriter = new XmlTextWriter (writer);
670 xwriter.Formatting = Formatting.Indented;
674 public void WriteXml (XmlWriter writer)
676 WriteXml (writer, XmlWriteMode.IgnoreSchema);
679 public void WriteXml (string filename, XmlWriteMode mode)
681 XmlTextWriter writer = new XmlTextWriter (filename, null);
682 writer.Formatting = Formatting.Indented;
683 writer.WriteStartDocument (true);
686 WriteXml (writer, mode);
689 writer.WriteEndDocument ();
694 public void WriteXml (Stream stream, XmlWriteMode mode)
696 XmlTextWriter writer = new XmlTextWriter (stream, null);
697 writer.Formatting = Formatting.Indented;
698 WriteXml (writer, mode);
701 public void WriteXml (TextWriter writer, XmlWriteMode mode)
703 XmlTextWriter xwriter = new XmlTextWriter (writer);
704 xwriter.Formatting = Formatting.Indented;
705 WriteXml (xwriter, mode);
708 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
710 if (mode == XmlWriteMode.DiffGram) {
712 WriteDiffGramElement(writer);
715 // It should not write when there is no content to be written
716 bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
717 for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
718 shouldOutputContent = tableCollection [n].Rows.Count > 0;
720 if (shouldOutputContent) {
721 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
723 if (mode == XmlWriteMode.WriteSchema)
724 DoWriteXmlSchema (writer);
726 WriteTables (writer, mode, Tables, DataRowVersion.Default);
727 writer.WriteEndElement ();
730 if (mode == XmlWriteMode.DiffGram) {
731 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
733 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
734 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
735 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
736 writer.WriteEndElement ();
740 if (mode == XmlWriteMode.DiffGram)
741 writer.WriteEndElement (); // diffgr:diffgram
746 public void WriteXmlSchema (Stream stream)
748 XmlTextWriter writer = new XmlTextWriter (stream, null );
749 writer.Formatting = Formatting.Indented;
750 WriteXmlSchema (writer);
753 public void WriteXmlSchema (string fileName)
755 XmlTextWriter writer = new XmlTextWriter (fileName, null);
757 writer.Formatting = Formatting.Indented;
758 writer.WriteStartDocument (true);
759 WriteXmlSchema (writer);
761 writer.WriteEndDocument ();
766 public void WriteXmlSchema (TextWriter writer)
768 XmlTextWriter xwriter = new XmlTextWriter (writer);
770 xwriter.Formatting = Formatting.Indented;
771 // xwriter.WriteStartDocument ();
772 WriteXmlSchema (xwriter);
774 // xwriter.WriteEndDocument ();
779 public void WriteXmlSchema (XmlWriter writer)
781 //Create a skeleton doc and then write the schema
782 //proper which is common to the WriteXml method in schema mode
783 DoWriteXmlSchema (writer);
786 public void ReadXmlSchema (Stream stream)
788 XmlReader reader = new XmlTextReader (stream, null);
789 ReadXmlSchema (reader);
792 public void ReadXmlSchema (string str)
794 XmlReader reader = new XmlTextReader (str);
796 ReadXmlSchema (reader);
803 public void ReadXmlSchema (TextReader treader)
805 XmlReader reader = new XmlTextReader (treader);
806 ReadXmlSchema (reader);
809 public void ReadXmlSchema (XmlReader reader)
812 new XmlSchemaDataImporter (this, reader).Process ();
814 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
815 SchemaMapper.Read (reader);
819 public XmlReadMode ReadXml (Stream stream)
821 return ReadXml (new XmlTextReader (stream));
824 public XmlReadMode ReadXml (string str)
826 XmlTextReader reader = new XmlTextReader (str);
828 return ReadXml (reader);
835 public XmlReadMode ReadXml (TextReader reader)
837 return ReadXml (new XmlTextReader (reader));
840 public XmlReadMode ReadXml (XmlReader r)
842 return ReadXml (r, XmlReadMode.Auto);
845 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
847 return ReadXml (new XmlTextReader (stream), mode);
850 public XmlReadMode ReadXml (string str, XmlReadMode mode)
852 XmlTextReader reader = new XmlTextReader (str);
854 return ReadXml (reader, mode);
861 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
863 return ReadXml (new XmlTextReader (reader), mode);
866 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
868 switch (reader.ReadState) {
869 case ReadState.EndOfFile:
870 case ReadState.Error:
871 case ReadState.Closed:
874 // Skip XML declaration and prolog
875 reader.MoveToContent();
879 XmlReadMode Result = mode;
881 // If diffgram, then read the first element as diffgram
882 if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
884 case XmlReadMode.Auto:
885 case XmlReadMode.DiffGram:
886 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
887 DiffLoader.Load (reader);
888 // (and leave rest of the reader as is)
889 return XmlReadMode.DiffGram;
890 case XmlReadMode.Fragment:
892 // (and continue to read)
896 // (and leave rest of the reader as is)
900 // If schema, then read the first element as schema
901 if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
903 case XmlReadMode.IgnoreSchema:
904 case XmlReadMode.InferSchema:
906 // (and break up read)
908 case XmlReadMode.Fragment:
909 ReadXmlSchema (reader);
910 // (and continue to read)
912 case XmlReadMode.Auto:
913 if (Tables.Count == 0) {
914 ReadXmlSchema (reader);
915 return XmlReadMode.ReadSchema;
917 // otherwise just ignore and return IgnoreSchema
919 return XmlReadMode.IgnoreSchema;
922 ReadXmlSchema (reader);
923 // (and leave rest of the reader as is)
924 return mode; // When DiffGram, return DiffGram
927 // Otherwise, read as dataset... but only when required.
928 XmlReadMode explicitReturnMode = XmlReadMode.Auto;
931 case XmlReadMode.Auto:
932 if (Tables.Count > 0)
933 goto case XmlReadMode.IgnoreSchema;
935 goto case XmlReadMode.InferSchema;
936 case XmlReadMode.InferSchema:
937 doc = new XmlDocument ();
939 doc.AppendChild (doc.ReadNode (reader));
940 reader.MoveToContent ();
941 if (doc.DocumentElement != null)
943 } while (!reader.EOF);
944 InferXmlSchema (doc, null);
945 reader = new XmlNodeReader (doc);
946 explicitReturnMode = XmlReadMode.InferSchema;
948 case XmlReadMode.ReadSchema:
949 doc = new XmlDocument ();
951 doc.AppendChild (doc.ReadNode (reader));
952 reader.MoveToContent ();
953 if (doc.DocumentElement != null)
955 } while (!reader.EOF);
956 if (doc.DocumentElement != null) {
957 XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
958 if (schema != null) {
959 ReadXmlSchema (new XmlNodeReader (schema));
960 explicitReturnMode = XmlReadMode.ReadSchema;
963 reader = new XmlNodeReader (doc);
965 case XmlReadMode.IgnoreSchema:
966 case XmlReadMode.Fragment:
973 XmlDataReader.ReadXml (this, reader, mode);
974 if (explicitReturnMode != XmlReadMode.Auto)
975 return explicitReturnMode;
976 return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
978 #endregion // Public Methods
980 #region Public Events
982 [DataCategory ("Action")]
983 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
984 public event MergeFailedEventHandler MergeFailed;
986 #endregion // Public Events
988 #region IListSource methods
989 IList IListSource.GetList ()
991 return DefaultViewManager;
994 bool IListSource.ContainsListCollection {
999 #endregion IListSource methods
1001 #region ISupportInitialize methods
1002 public void BeginInit ()
1006 public void EndInit ()
1011 #region ISerializable
1012 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1014 StringWriter sw = new StringWriter ();
1015 XmlTextWriter writer = new XmlTextWriter (sw);
1016 DoWriteXmlSchema (writer);
1018 si.AddValue ("XmlSchema", sw.ToString ());
1020 sw = new StringWriter ();
1021 writer = new XmlTextWriter (sw);
1022 WriteXml (writer, XmlWriteMode.DiffGram);
1024 si.AddValue ("XmlDiffGram", sw.ToString ());
1028 #region Protected Methods
1029 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1031 string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1032 XmlTextReader reader = new XmlTextReader (new StringReader (s));
1033 ReadXmlSchema (reader);
1036 s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1037 reader = new XmlTextReader (new StringReader (s));
1038 ReadXml (reader, XmlReadMode.DiffGram);
1043 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1048 protected virtual void ReadXmlSerializable (XmlReader reader)
1050 reader.MoveToContent ();
1051 reader.ReadStartElement ();
1052 reader.MoveToContent ();
1053 ReadXmlSchema (reader);
1054 reader.MoveToContent ();
1055 ReadXml (reader, XmlReadMode.DiffGram);
1056 reader.MoveToContent ();
1057 reader.ReadEndElement ();
1060 void IXmlSerializable.ReadXml (XmlReader reader)
1062 ReadXmlSerializable(reader);
1065 void IXmlSerializable.WriteXml (XmlWriter writer)
1067 DoWriteXmlSchema (writer);
1068 WriteXml (writer, XmlWriteMode.DiffGram);
1071 XmlSchema IXmlSerializable.GetSchema ()
1073 return BuildSchema ();
1076 protected virtual bool ShouldSerializeRelations ()
1081 protected virtual bool ShouldSerializeTables ()
1087 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1092 protected virtual void OnRemoveRelation (DataRelation relation)
1097 protected virtual void OnRemoveTable (DataTable table)
1101 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1103 if (MergeFailed != null)
1104 MergeFailed (this, e);
1108 protected internal void RaisePropertyChanging (string name)
1113 #region Private Xml Serialisation
1115 private string WriteObjectXml (object o)
1117 switch (Type.GetTypeCode (o.GetType ())) {
1118 case TypeCode.Boolean:
1119 return XmlConvert.ToString ((Boolean) o);
1121 return XmlConvert.ToString ((Byte) o);
1123 return XmlConvert.ToString ((Char) o);
1124 case TypeCode.DateTime:
1125 return XmlConvert.ToString ((DateTime) o);
1126 case TypeCode.Decimal:
1127 return XmlConvert.ToString ((Decimal) o);
1128 case TypeCode.Double:
1129 return XmlConvert.ToString ((Double) o);
1130 case TypeCode.Int16:
1131 return XmlConvert.ToString ((Int16) o);
1132 case TypeCode.Int32:
1133 return XmlConvert.ToString ((Int32) o);
1134 case TypeCode.Int64:
1135 return XmlConvert.ToString ((Int64) o);
1136 case TypeCode.SByte:
1137 return XmlConvert.ToString ((SByte) o);
1138 case TypeCode.Single:
1139 return XmlConvert.ToString ((Single) o);
1140 case TypeCode.UInt16:
1141 return XmlConvert.ToString ((UInt16) o);
1142 case TypeCode.UInt32:
1143 return XmlConvert.ToString ((UInt32) o);
1144 case TypeCode.UInt64:
1145 return XmlConvert.ToString ((UInt64) o);
1147 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1148 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1149 if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1150 return o.ToString ();
1153 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1155 //Write out each table in order, providing it is not
1156 //part of another table structure via a nested parent relationship
1157 foreach (DataTable table in tableCollection) {
1158 bool isTopLevel = true;
1160 foreach (DataRelation rel in table.ParentRelations) {
1168 WriteTable ( writer, table, mode, version);
1173 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1175 DataRow[] rows = new DataRow [table.Rows.Count];
1176 table.Rows.CopyTo (rows, 0);
1177 WriteTable (writer, rows, mode, version, true);
1180 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
1182 //The columns can be attributes, hidden, elements, or simple content
1183 //There can be 0-1 simple content cols or 0-* elements
1184 System.Collections.ArrayList atts;
1185 System.Collections.ArrayList elements;
1186 DataColumn simple = null;
1188 if (rows.Length == 0) return;
1189 DataTable table = rows[0].Table;
1190 SplitColumns (table, out atts, out elements, out simple);
1191 //sort out the namespacing
1192 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1193 int relationCount = table.ParentRelations.Count;
1194 DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1196 foreach (DataRow row in rows) {
1198 // Skip rows that is a child of any tables.
1199 switch (relationCount) {
1205 if (row.GetParentRow (oneRel) != null)
1210 for (int i = 0; i < table.ParentRelations.Count; i++) {
1211 DataRelation prel = table.ParentRelations [i];
1214 if (row.GetParentRow (prel) != null) {
1225 if (!row.HasVersion(version) ||
1226 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1227 && version == DataRowVersion.Original))
1230 // First check are all the rows null. If they are we just write empty element
1231 bool AllNulls = true;
1232 foreach (DataColumn dc in table.Columns) {
1234 if (row [dc.ColumnName, version] != DBNull.Value) {
1240 // If all of the columns were null, we have to write empty element
1242 writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1246 WriteTableElement (writer, mode, table, row, version);
1248 foreach (DataColumn col in atts) {
1249 WriteColumnAsAttribute (writer, mode, col, row, version);
1252 if (simple != null) {
1253 writer.WriteString (WriteObjectXml (row[simple, version]));
1256 foreach (DataColumn col in elements) {
1257 WriteColumnAsElement (writer, mode, col, row, version);
1261 foreach (DataRelation relation in table.ChildRelations) {
1262 if (relation.Nested) {
1263 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1267 writer.WriteEndElement ();
1272 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1274 string colnspc = null;
1275 object rowObject = row [col, version];
1277 if (rowObject == null || rowObject == DBNull.Value)
1280 if (col.Namespace != String.Empty)
1281 colnspc = col.Namespace;
1283 //TODO check if I can get away with write element string
1284 WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1285 writer.WriteString (WriteObjectXml (rowObject));
1286 writer.WriteEndElement ();
1289 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1291 if (!row.IsNull (col))
1292 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1295 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1297 //sort out the namespacing
1298 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1300 WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1302 if (mode == XmlWriteMode.DiffGram) {
1303 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1304 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1305 string modeName = null;
1306 if (row.RowState == DataRowState.Modified)
1307 modeName = "modified";
1308 else if (row.RowState == DataRowState.Added)
1309 modeName = "inserted";
1311 if (version != DataRowVersion.Original && modeName != null)
1312 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1316 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1318 writer.WriteStartElement (prefix, name, nspc);
1321 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1324 case XmlWriteMode.WriteSchema:
1325 writer.WriteAttributeString (prefix, name, nspc);
1327 case XmlWriteMode.DiffGram:
1328 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1331 writer.WriteAttributeString (name, stringValue);
1336 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1338 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1340 if (mode == XmlWriteMode.DiffGram) {
1341 SetTableRowsID (table);
1342 WriteDiffGramElement (writer);
1345 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1347 WriteTable (writer, table, mode, DataRowVersion.Default);
1349 if (mode == XmlWriteMode.DiffGram) {
1350 writer.WriteEndElement (); //DataSet name
1351 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1353 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1354 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1355 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1356 writer.WriteEndElement ();
1359 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1363 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1365 if (ns == String.Empty)
1367 if (ns != nsmgr.DefaultNamespace) {
1368 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1369 for (int i = 1; i < int.MaxValue; i++) {
1370 string p = nsmgr.NameTable.Add ("app" + i);
1371 if (!nsmgr.HasNamespace (p)) {
1372 nsmgr.AddNamespace (p, ns);
1373 HandleExternalNamespace (p, ns, schema);
1381 XmlSchema BuildSchema ()
1383 return BuildSchema (Tables, Relations);
1386 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1388 string constraintPrefix = "";
1389 XmlSchema schema = new XmlSchema ();
1390 XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1392 if (Namespace != "") {
1393 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1394 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1395 schema.TargetNamespace = Namespace;
1396 constraintPrefix = XmlConstants.TnsPrefix + ":";
1399 // set the schema id
1400 string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1401 schema.Id = DataSetName;
1402 XmlDocument doc = new XmlDocument ();
1403 XmlAttribute attr = null;
1404 ArrayList atts = new ArrayList ();
1406 attr = doc.CreateAttribute ("", "xmlns", xmlNSURI);
1409 nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1410 nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1411 if (Namespace != "") {
1412 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1413 nsmgr.AddNamespace (String.Empty, Namespace);
1415 if (CheckExtendedPropertyExists ())
1416 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1419 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1421 XmlSchemaElement elem = new XmlSchemaElement ();
1422 elem.Name = XmlConvert.EncodeName (DataSetName);
1424 // Add namespaces used in DataSet components (tables, columns, ...)
1425 foreach (DataTable dt in Tables) {
1426 foreach (DataColumn col in dt.Columns)
1427 CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1428 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1431 // Attributes for DataSet element
1433 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1434 attr.Value = "true";
1437 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1438 attr.Value = locale.Name;
1441 elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1443 AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1445 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1446 elem.SchemaType = complex;
1448 XmlSchemaChoice choice = new XmlSchemaChoice ();
1449 complex.Particle = choice;
1450 choice.MaxOccursString = XmlConstants.Unbounded;
1452 //Write out schema for each table in order
1453 foreach (DataTable table in tables) {
1454 bool isTopLevel = true;
1455 foreach (DataRelation rel in table.ParentRelations) {
1463 if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1464 XmlSchemaElement extElem = new XmlSchemaElement ();
1465 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1466 choice.Items.Add (extElem);
1469 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1473 schema.Items.Add (elem);
1475 AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1476 foreach (string prefix in nsmgr) {
1477 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1478 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1479 schema.Namespaces.Add (prefix, ns);
1484 private bool CheckExtendedPropertyExists ()
1486 if (ExtendedProperties.Count > 0)
1488 foreach (DataTable dt in Tables) {
1489 if (dt.ExtendedProperties.Count > 0)
1491 foreach (DataColumn col in dt.Columns)
1492 if (col.ExtendedProperties.Count > 0)
1494 foreach (Constraint c in dt.Constraints)
1495 if (c.ExtendedProperties.Count > 0)
1498 foreach (DataRelation rel in Relations)
1499 if (rel.ExtendedProperties.Count > 0)
1504 // Add all constraints in all tables to the schema.
1505 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
1507 // first add all unique constraints.
1508 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1509 // Add all foriegn key constraints.
1510 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1513 // Add unique constaraints to the schema.
1514 // return hashtable with the names of all XmlSchemaUnique elements we created.
1515 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1517 Hashtable uniqueNames = new Hashtable();
1518 foreach (DataTable table in tables) {
1520 foreach (Constraint constraint in table.Constraints) {
1522 if (constraint is UniqueConstraint) {
1523 ArrayList attrs = new ArrayList ();
1524 XmlAttribute attrib;
1525 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1526 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1528 // if column of the constraint is hidden do not write the constraint.
1529 bool isHidden = false;
1530 foreach (DataColumn column in uqConst.Columns) {
1531 if (column.ColumnMapping == MappingType.Hidden) {
1540 // if constaraint name do not exist in the hashtable we can use it.
1541 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1542 uniq.Name = uqConst.ConstraintName;
1544 // generate new constraint name for the XmlSchemaUnique element.
1546 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1547 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1548 attrib.Value = uqConst.ConstraintName;
1551 if (uqConst.IsPrimaryKey) {
1552 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1553 attrib.Value = "true";
1557 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1559 uniq.Selector = new XmlSchemaXPath();
1560 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1561 XmlSchemaXPath field;
1562 foreach (DataColumn column in uqConst.Columns) {
1563 field = new XmlSchemaXPath();
1564 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1565 field.XPath = typePrefix + constraintPrefix+column.ColumnName;
1566 uniq.Fields.Add(field);
1569 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1571 elem.Constraints.Add (uniq);
1572 uniqueNames.Add (uniq.Name, null);
1579 // Add the foriegn keys to the schema.
1580 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1582 if (relations == null) return;
1584 foreach (DataRelation rel in relations) {
1586 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1589 bool isHidden = false;
1590 foreach (DataColumn col in rel.ParentColumns) {
1591 if (col.ColumnMapping == MappingType.Hidden) {
1596 foreach (DataColumn col in rel.ChildColumns) {
1597 if (col.ColumnMapping == MappingType.Hidden) {
1605 ArrayList attrs = new ArrayList ();
1606 XmlAttribute attrib;
1607 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1608 keyRef.Name = rel.RelationName;
1609 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1610 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1612 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1613 // first try to find the concatenated name. If we didn't find it - use constraint name.
1614 if (uniqueNames.ContainsKey (concatName)) {
1615 keyRef.Refer = new XmlQualifiedName(concatName);
1618 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1622 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1623 attrib.Value = "true";
1627 keyRef.Selector = new XmlSchemaXPath();
1628 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1629 XmlSchemaXPath field;
1630 foreach (DataColumn column in rel.ChildColumns) {
1631 field = new XmlSchemaXPath();
1632 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1633 field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1634 keyRef.Fields.Add(field);
1637 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1638 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1640 elem.Constraints.Add (keyRef);
1644 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1650 ArrayList xattrs = new ArrayList();
1653 SplitColumns (table, out atts, out elements, out simple);
1655 XmlSchemaElement elem = new XmlSchemaElement ();
1656 elem.Name = table.TableName;
1658 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1659 elem.SchemaType = complex;
1661 XmlSchemaObjectCollection schemaAttributes = null;
1663 if (simple != null) {
1664 // add simpleContent
1665 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1666 complex.ContentModel = simpleContent;
1668 // add column name attribute
1669 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1670 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1671 xlmAttrs[0].Value = simple.ColumnName;
1673 // add ordinal attribute
1674 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1675 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1676 simpleContent.UnhandledAttributes = xlmAttrs;
1680 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1681 simpleContent.Content = extension;
1682 extension.BaseTypeName = MapType (simple.DataType);
1683 schemaAttributes = extension.Attributes;
1685 schemaAttributes = complex.Attributes;
1686 //A sequence of element types or a simple content node
1688 XmlSchemaSequence seq = new XmlSchemaSequence ();
1690 foreach (DataColumn col in elements) {
1692 // Add element for the column.
1693 XmlSchemaElement colElem = new XmlSchemaElement ();
1694 colElem.Name = col.ColumnName;
1696 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1697 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1698 xattr.Value = col.Caption;
1702 if (col.AutoIncrement == true) {
1703 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1704 xattr.Value = "true";
1708 if (col.AutoIncrementSeed != 0) {
1709 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1710 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1714 if (col.DefaultValue.ToString () != String.Empty)
1715 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1718 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1719 xattr.Value = "true";
1723 if (col.MaxLength < 0)
1724 colElem.SchemaTypeName = MapType (col.DataType);
1726 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1727 && col.DataType != typeof (char)) {
1728 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1729 xattr.Value = col.DataType.AssemblyQualifiedName;
1733 if (col.AllowDBNull) {
1734 colElem.MinOccurs = 0;
1737 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1738 // XmlConstants.Ordinal,
1739 // XmlConstants.MsdataNamespace,
1740 // col.Ordinal.ToString ());
1742 // Write SimpleType if column have MaxLength
1743 if (col.MaxLength > -1) {
1744 colElem.SchemaType = GetTableSimpleType (doc, col);
1747 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1749 AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1750 seq.Items.Add (colElem);
1753 foreach (DataRelation rel in table.ChildRelations) {
1755 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1756 XmlSchemaElement el = new XmlSchemaElement ();
1757 el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1759 XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1761 el.MaxOccursString = "unbounded";
1762 XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1764 el.SchemaType = null;
1765 el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1766 schemaToAdd.Items.Add (ct);
1772 if (seq.Items.Count > 0)
1773 complex.Particle = seq;
1776 //Then a list of attributes
1777 foreach (DataColumn col in atts) {
1778 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1779 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1780 att.Name = col.ColumnName;
1781 if (col.Namespace != String.Empty) {
1782 att.Form = XmlSchemaForm.Qualified;
1783 string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1784 att.Name = prefix + ":" + col.ColumnName;
1785 // FIXME: Handle prefix mapping correctly.
1786 schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1788 if (!col.AllowDBNull)
1789 att.Use = XmlSchemaUse.Required;
1790 if (col.DefaultValue.ToString () != String.Empty)
1791 att.DefaultValue = WriteObjectXml (col.DefaultValue);
1794 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1795 xattr.Value = "true";
1799 att.UnhandledAttributes = xattrs.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1802 if (col.MaxLength > -1)
1803 att.SchemaType = GetTableSimpleType (doc, col);
1805 att.SchemaTypeName = MapType (col.DataType);
1806 // FIXME: what happens if extended properties are set on attribute columns??
1807 schemaAttributes.Add (att);
1810 AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1815 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1817 ArrayList attList = new ArrayList ();
1818 XmlAttribute xmlAttr;
1820 if (xsobj.UnhandledAttributes != null)
1821 attList.AddRange (xsobj.UnhandledAttributes);
1823 // add extended properties to xs:element
1824 foreach (DictionaryEntry de in props) {
1825 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1826 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1827 attList.Add (xmlAttr);
1829 if (attList.Count > 0)
1830 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1833 private string SafeNS (string ns)
1835 return ns != null ? ns : String.Empty;
1838 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1840 foreach (XmlSchemaExternal ext in schema.Includes) {
1841 XmlSchemaImport imp = ext as XmlSchemaImport;
1842 if (imp != null && imp.Namespace == ns)
1843 return; // nothing to do
1845 XmlSchemaImport i = new XmlSchemaImport ();
1847 i.SchemaLocation = "_" + prefix + ".xsd";
1848 schema.Includes.Add (i);
1851 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1854 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1857 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1858 restriction.BaseTypeName = MapType (col.DataType);
1861 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1862 max.Value = XmlConvert.ToString (col.MaxLength);
1863 restriction.Facets.Add (max);
1865 simple.Content = restriction;
1869 private void DoWriteXmlSchema (XmlWriter writer)
1871 BuildSchema ().Write (writer);
1875 /// Helper function to split columns into attributes elements and simple
1878 private void SplitColumns (DataTable table,
1880 out ArrayList elements,
1881 out DataColumn simple)
1883 //The columns can be attributes, hidden, elements, or simple content
1884 //There can be 0-1 simple content cols or 0-* elements
1885 atts = new System.Collections.ArrayList ();
1886 elements = new System.Collections.ArrayList ();
1889 //Sort out the columns
1890 foreach (DataColumn col in table.Columns) {
1891 switch (col.ColumnMapping) {
1892 case MappingType.Attribute:
1895 case MappingType.Element:
1898 case MappingType.SimpleContent:
1899 if (simple != null) {
1900 throw new System.InvalidOperationException ("There may only be one simple content element");
1905 //ignore Hidden elements
1911 private void WriteDiffGramElement(XmlWriter writer)
1913 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1914 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1917 private void SetRowsID()
1919 foreach (DataTable Table in Tables)
1920 SetTableRowsID (Table);
1923 private void SetTableRowsID (DataTable Table)
1926 foreach (DataRow Row in Table.Rows) {
1927 Row.XmlRowID = dataRowID;
1933 private XmlQualifiedName MapType (Type type)
1935 switch (Type.GetTypeCode (type)) {
1936 case TypeCode.String: return XmlConstants.QnString;
1937 case TypeCode.Int16: return XmlConstants.QnShort;
1938 case TypeCode.Int32: return XmlConstants.QnInt;
1939 case TypeCode.Int64: return XmlConstants.QnLong;
1940 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1941 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1942 //case TypeCode.Char: return XmlConstants.QnChar;
1943 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1944 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1945 case TypeCode.Double: return XmlConstants.QnDouble;
1946 case TypeCode.SByte: return XmlConstants.QnSbyte;
1947 case TypeCode.Single: return XmlConstants.QnFloat;
1948 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1949 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1950 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1953 if (typeof (TimeSpan) == type)
1954 return XmlConstants.QnDuration;
1955 else if (typeof (System.Uri) == type)
1956 return XmlConstants.QnUri;
1957 else if (typeof (byte[]) == type)
1958 return XmlConstants.QnBase64Binary;
1959 else if (typeof (XmlQualifiedName) == type)
1960 return XmlConstants.QnXmlQualifiedName;
1962 return XmlConstants.QnString;
1965 #endregion //Private Xml Serialisation