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) {
1197 if (row.GetParentRow (oneRel) != null)
1202 for (int i = 0; i < table.ParentRelations.Count; i++)
1203 if (row.GetParentRow (table.ParentRelations [i]) != null) {
1213 if (!row.HasVersion(version) ||
1214 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1215 && version == DataRowVersion.Original))
1218 // First check are all the rows null. If they are we just write empty element
1219 bool AllNulls = true;
1220 foreach (DataColumn dc in table.Columns) {
1222 if (row [dc.ColumnName, version] != DBNull.Value) {
1228 // If all of the columns were null, we have to write empty element
1230 writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1234 WriteTableElement (writer, mode, table, row, version);
1236 foreach (DataColumn col in atts) {
1237 WriteColumnAsAttribute (writer, mode, col, row, version);
1240 if (simple != null) {
1241 writer.WriteString (WriteObjectXml (row[simple, version]));
1244 foreach (DataColumn col in elements) {
1245 WriteColumnAsElement (writer, mode, col, row, version);
1249 foreach (DataRelation relation in table.ChildRelations) {
1250 if (relation.Nested) {
1251 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1255 writer.WriteEndElement ();
1260 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1262 string colnspc = null;
1263 object rowObject = row [col, version];
1265 if (rowObject == null || rowObject == DBNull.Value)
1268 if (col.Namespace != String.Empty)
1269 colnspc = col.Namespace;
1271 //TODO check if I can get away with write element string
1272 WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1273 writer.WriteString (WriteObjectXml (rowObject));
1274 writer.WriteEndElement ();
1277 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1279 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1282 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1284 //sort out the namespacing
1285 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1287 WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1289 if (mode == XmlWriteMode.DiffGram) {
1290 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1291 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1292 string modeName = null;
1293 if (row.RowState == DataRowState.Modified)
1294 modeName = "modified";
1295 else if (row.RowState == DataRowState.Added)
1296 modeName = "inserted";
1298 if (version != DataRowVersion.Original && modeName != null)
1299 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1303 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1305 writer.WriteStartElement (prefix, name, nspc);
1308 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1311 case XmlWriteMode.WriteSchema:
1312 writer.WriteAttributeString (prefix, name, nspc);
1314 case XmlWriteMode.DiffGram:
1315 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1318 writer.WriteAttributeString (name, stringValue);
1323 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1325 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1327 if (mode == XmlWriteMode.DiffGram) {
1328 SetTableRowsID (table);
1329 WriteDiffGramElement (writer);
1332 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1334 WriteTable (writer, table, mode, DataRowVersion.Default);
1336 if (mode == XmlWriteMode.DiffGram) {
1337 writer.WriteEndElement (); //DataSet name
1338 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1340 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1341 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1342 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1343 writer.WriteEndElement ();
1346 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1350 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1352 if (ns == String.Empty)
1354 if (ns != nsmgr.DefaultNamespace) {
1355 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1356 for (int i = 1; i < int.MaxValue; i++) {
1357 string p = nsmgr.NameTable.Add ("app" + i);
1358 if (!nsmgr.HasNamespace (p)) {
1359 nsmgr.AddNamespace (p, ns);
1360 HandleExternalNamespace (p, ns, schema);
1368 XmlSchema BuildSchema ()
1370 return BuildSchema (Tables, Relations);
1373 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1375 string constraintPrefix = "";
1376 XmlSchema schema = new XmlSchema ();
1377 XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1379 if (Namespace != "") {
1380 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1381 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1382 schema.TargetNamespace = Namespace;
1383 constraintPrefix = XmlConstants.TnsPrefix + ":";
1386 // set the schema id
1387 string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1388 schema.Id = DataSetName;
1389 XmlDocument doc = new XmlDocument ();
1390 XmlAttribute attr = null;
1391 ArrayList atts = new ArrayList ();
1393 nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1394 nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1395 if (Namespace != "") {
1396 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1397 nsmgr.AddNamespace (String.Empty, Namespace);
1399 if (CheckExtendedPropertyExists ())
1400 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1403 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1405 XmlSchemaElement elem = new XmlSchemaElement ();
1406 elem.Name = XmlConvert.EncodeName (DataSetName);
1408 // Add namespaces used in DataSet components (tables, columns, ...)
1409 foreach (DataTable dt in Tables) {
1410 foreach (DataColumn col in dt.Columns)
1411 CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1412 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1415 // Attributes for DataSet element
1417 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1418 attr.Value = "true";
1421 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1422 attr.Value = locale.Name;
1425 elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1427 AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1429 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1430 elem.SchemaType = complex;
1432 XmlSchemaChoice choice = new XmlSchemaChoice ();
1433 complex.Particle = choice;
1434 choice.MaxOccursString = XmlConstants.Unbounded;
1436 //Write out schema for each table in order
1437 foreach (DataTable table in tables) {
1438 bool isTopLevel = true;
1439 foreach (DataRelation rel in table.ParentRelations) {
1447 if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1448 XmlSchemaElement extElem = new XmlSchemaElement ();
1449 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1450 choice.Items.Add (extElem);
1453 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1457 schema.Items.Add (elem);
1459 AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1460 foreach (string prefix in nsmgr) {
1461 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1462 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1463 schema.Namespaces.Add (prefix, ns);
1468 private bool CheckExtendedPropertyExists ()
1470 if (ExtendedProperties.Count > 0)
1472 foreach (DataTable dt in Tables) {
1473 if (dt.ExtendedProperties.Count > 0)
1475 foreach (DataColumn col in dt.Columns)
1476 if (col.ExtendedProperties.Count > 0)
1478 foreach (Constraint c in dt.Constraints)
1479 if (c.ExtendedProperties.Count > 0)
1482 foreach (DataRelation rel in Relations)
1483 if (rel.ExtendedProperties.Count > 0)
1488 // Add all constraints in all tables to the schema.
1489 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
1491 // first add all unique constraints.
1492 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1493 // Add all foriegn key constraints.
1494 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1497 // Add unique constaraints to the schema.
1498 // return hashtable with the names of all XmlSchemaUnique elements we created.
1499 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1501 Hashtable uniqueNames = new Hashtable();
1502 foreach (DataTable table in tables) {
1504 foreach (Constraint constraint in table.Constraints) {
1506 if (constraint is UniqueConstraint) {
1507 ArrayList attrs = new ArrayList ();
1508 XmlAttribute attrib;
1509 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1510 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1512 // if column of the constraint is hidden do not write the constraint.
1513 bool isHidden = false;
1514 foreach (DataColumn column in uqConst.Columns) {
1515 if (column.ColumnMapping == MappingType.Hidden) {
1524 // if constaraint name do not exist in the hashtable we can use it.
1525 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1526 uniq.Name = uqConst.ConstraintName;
1528 // generate new constraint name for the XmlSchemaUnique element.
1530 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1531 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1532 attrib.Value = uqConst.ConstraintName;
1535 if (uqConst.IsPrimaryKey) {
1536 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1537 attrib.Value = "true";
1541 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1543 uniq.Selector = new XmlSchemaXPath();
1544 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1545 XmlSchemaXPath field;
1546 foreach (DataColumn column in uqConst.Columns) {
1547 field = new XmlSchemaXPath();
1548 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1549 field.XPath = typePrefix + constraintPrefix+column.ColumnName;
1550 uniq.Fields.Add(field);
1553 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1555 elem.Constraints.Add (uniq);
1556 uniqueNames.Add (uniq.Name, null);
1563 // Add the foriegn keys to the schema.
1564 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1566 if (relations == null) return;
1568 foreach (DataRelation rel in relations) {
1570 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1573 bool isHidden = false;
1574 foreach (DataColumn col in rel.ParentColumns) {
1575 if (col.ColumnMapping == MappingType.Hidden) {
1580 foreach (DataColumn col in rel.ChildColumns) {
1581 if (col.ColumnMapping == MappingType.Hidden) {
1589 ArrayList attrs = new ArrayList ();
1590 XmlAttribute attrib;
1591 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1592 keyRef.Name = rel.RelationName;
1593 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1594 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1596 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1597 // first try to find the concatenated name. If we didn't find it - use constraint name.
1598 if (uniqueNames.ContainsKey (concatName)) {
1599 keyRef.Refer = new XmlQualifiedName(concatName);
1602 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1606 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1607 attrib.Value = "true";
1611 keyRef.Selector = new XmlSchemaXPath();
1612 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1613 XmlSchemaXPath field;
1614 foreach (DataColumn column in rel.ChildColumns) {
1615 field = new XmlSchemaXPath();
1616 string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1617 field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1618 keyRef.Fields.Add(field);
1621 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1622 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1624 elem.Constraints.Add (keyRef);
1628 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1634 SplitColumns (table, out atts, out elements, out simple);
1636 XmlSchemaElement elem = new XmlSchemaElement ();
1637 elem.Name = table.TableName;
1639 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1640 elem.SchemaType = complex;
1642 XmlSchemaObjectCollection schemaAttributes = null;
1644 if (simple != null) {
1645 // add simpleContent
1646 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1647 complex.ContentModel = simpleContent;
1649 // add column name attribute
1650 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1651 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1652 xlmAttrs[0].Value = simple.ColumnName;
1654 // add ordinal attribute
1655 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1656 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1657 simpleContent.UnhandledAttributes = xlmAttrs;
1661 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1662 simpleContent.Content = extension;
1663 extension.BaseTypeName = MapType (simple.DataType);
1664 schemaAttributes = extension.Attributes;
1666 schemaAttributes = complex.Attributes;
1667 //A sequence of element types or a simple content node
1669 XmlSchemaSequence seq = new XmlSchemaSequence ();
1671 foreach (DataColumn col in elements) {
1673 // Add element for the column.
1674 XmlSchemaElement colElem = new XmlSchemaElement ();
1675 ArrayList xattrs = new ArrayList();
1677 colElem.Name = col.ColumnName;
1679 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1680 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1681 xattr.Value = col.Caption;
1685 if (col.AutoIncrement == true) {
1686 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1687 xattr.Value = "true";
1691 if (col.AutoIncrementSeed != 0) {
1692 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1693 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1697 if (col.DefaultValue.ToString () != String.Empty)
1698 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1700 if (col.MaxLength < 0)
1701 colElem.SchemaTypeName = MapType (col.DataType);
1703 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1704 && col.DataType != typeof (char)) {
1705 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1706 xattr.Value = col.DataType.AssemblyQualifiedName;
1710 if (col.AllowDBNull) {
1711 colElem.MinOccurs = 0;
1714 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1715 // XmlConstants.Ordinal,
1716 // XmlConstants.MsdataNamespace,
1717 // col.Ordinal.ToString ());
1719 // Write SimpleType if column have MaxLength
1720 if (col.MaxLength > -1) {
1721 colElem.SchemaType = GetTableSimpleType (doc, col);
1724 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1725 AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1726 seq.Items.Add (colElem);
1729 foreach (DataRelation rel in table.ChildRelations) {
1731 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1732 XmlSchemaElement el = new XmlSchemaElement ();
1733 el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1735 XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1737 el.MaxOccursString = "unbounded";
1738 XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1740 el.SchemaType = null;
1741 el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1742 schemaToAdd.Items.Add (ct);
1748 if (seq.Items.Count > 0)
1749 complex.Particle = seq;
1752 //Then a list of attributes
1753 foreach (DataColumn col in atts) {
1754 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1755 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1756 att.Name = col.ColumnName;
1757 if (col.Namespace != String.Empty) {
1758 att.Form = XmlSchemaForm.Qualified;
1759 string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1760 att.Name = prefix + ":" + col.ColumnName;
1761 // FIXME: Handle prefix mapping correctly.
1762 schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1764 att.SchemaTypeName = MapType (col.DataType);
1765 // FIXME: what happens if extended properties are set on attribute columns??
1766 schemaAttributes.Add (att);
1769 AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1774 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1776 ArrayList attList = new ArrayList ();
1777 XmlAttribute xmlAttr;
1779 if (xsobj.UnhandledAttributes != null)
1780 attList.AddRange (xsobj.UnhandledAttributes);
1782 // add extended properties to xs:element
1783 foreach (DictionaryEntry de in props) {
1784 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1785 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1786 attList.Add (xmlAttr);
1788 if (attList.Count > 0)
1789 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1792 private string SafeNS (string ns)
1794 return ns != null ? ns : String.Empty;
1797 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1799 foreach (XmlSchemaExternal ext in schema.Includes) {
1800 XmlSchemaImport imp = ext as XmlSchemaImport;
1801 if (imp != null && imp.Namespace == ns)
1802 return; // nothing to do
1804 XmlSchemaImport i = new XmlSchemaImport ();
1806 i.SchemaLocation = "_" + prefix + ".xsd";
1807 schema.Includes.Add (i);
1810 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1813 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1816 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1817 restriction.BaseTypeName = MapType (col.DataType);
1820 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1821 max.Value = XmlConvert.ToString (col.MaxLength);
1822 restriction.Facets.Add (max);
1824 simple.Content = restriction;
1828 private void DoWriteXmlSchema (XmlWriter writer)
1830 BuildSchema ().Write (writer);
1834 /// Helper function to split columns into attributes elements and simple
1837 private void SplitColumns (DataTable table,
1839 out ArrayList elements,
1840 out DataColumn simple)
1842 //The columns can be attributes, hidden, elements, or simple content
1843 //There can be 0-1 simple content cols or 0-* elements
1844 atts = new System.Collections.ArrayList ();
1845 elements = new System.Collections.ArrayList ();
1848 //Sort out the columns
1849 foreach (DataColumn col in table.Columns) {
1850 switch (col.ColumnMapping) {
1851 case MappingType.Attribute:
1854 case MappingType.Element:
1857 case MappingType.SimpleContent:
1858 if (simple != null) {
1859 throw new System.InvalidOperationException ("There may only be one simple content element");
1864 //ignore Hidden elements
1870 private void WriteDiffGramElement(XmlWriter writer)
1872 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1873 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1876 private void SetRowsID()
1878 foreach (DataTable Table in Tables)
1879 SetTableRowsID (Table);
1882 private void SetTableRowsID (DataTable Table)
1885 foreach (DataRow Row in Table.Rows) {
1886 Row.XmlRowID = dataRowID;
1892 private XmlQualifiedName MapType (Type type)
1894 switch (Type.GetTypeCode (type)) {
1895 case TypeCode.String: return XmlConstants.QnString;
1896 case TypeCode.Int16: return XmlConstants.QnShort;
1897 case TypeCode.Int32: return XmlConstants.QnInt;
1898 case TypeCode.Int64: return XmlConstants.QnLong;
1899 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1900 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1901 //case TypeCode.Char: return XmlConstants.QnChar;
1902 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1903 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1904 case TypeCode.Double: return XmlConstants.QnDouble;
1905 case TypeCode.SByte: return XmlConstants.QnSbyte;
1906 case TypeCode.Single: return XmlConstants.QnFloat;
1907 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1908 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1909 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1912 if (typeof (TimeSpan) == type)
1913 return XmlConstants.QnDuration;
1914 else if (typeof (System.Uri) == type)
1915 return XmlConstants.QnUri;
1916 else if (typeof (byte[]) == type)
1917 return XmlConstants.QnBase64Binary;
1918 else if (typeof (XmlQualifiedName) == type)
1919 return XmlConstants.QnXmlQualifiedName;
1921 return XmlConstants.QnString;
1924 #endregion //Private Xml Serialisation