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 ();
335 if (_xmlDataDocument != null)
336 throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
337 for (int t = 0; t < tableCollection.Count; t++) {
338 tableCollection[t].Clear ();
342 public virtual DataSet Clone ()
344 DataSet Copy = new DataSet ();
345 CopyProperties (Copy);
347 foreach (DataTable Table in Tables) {
348 Copy.Tables.Add (Table.Clone ());
351 //Copy Relationships between tables after existance of tables
352 //and setting properties correctly
353 CopyRelations (Copy);
358 // Copies both the structure and data for this DataSet.
359 public DataSet Copy ()
361 DataSet Copy = new DataSet ();
362 CopyProperties (Copy);
364 // Copy DatSet's tables
365 foreach (DataTable Table in Tables)
366 Copy.Tables.Add (Table.Copy ());
368 //Copy Relationships between tables after existance of tables
369 //and setting properties correctly
370 CopyRelations (Copy);
375 private void CopyProperties (DataSet Copy)
377 Copy.CaseSensitive = CaseSensitive;
378 //Copy.Container = Container
379 Copy.DataSetName = DataSetName;
380 //Copy.DefaultViewManager
382 Copy.EnforceConstraints = EnforceConstraints;
383 if(ExtendedProperties.Count > 0) {
384 // Cannot copy extended properties directly as the property does not have a set accessor
385 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
386 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
387 for (int i=0; i < ExtendedProperties.Count; i++)
388 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
390 Copy.Locale = Locale;
391 Copy.Namespace = Namespace;
392 Copy.Prefix = Prefix;
393 //Copy.Site = Site; // FIXME : Not sure of this.
398 private void CopyRelations (DataSet Copy)
401 //Creation of the relation contains some of the properties, and the constructor
402 //demands these values. instead changing the DataRelation constructor and behaviour the
403 //parameters are pre-configured and sent to the most general constructor
405 foreach (DataRelation MyRelation in this.Relations) {
406 string pTable = MyRelation.ParentTable.TableName;
407 string cTable = MyRelation.ChildTable.TableName;
408 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
409 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
412 foreach (DataColumn DC in MyRelation.ParentColumns) {
413 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
419 foreach (DataColumn DC in MyRelation.ChildColumns) {
420 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
424 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
425 //cRel.ChildColumns = MyRelation.ChildColumns;
426 //cRel.ChildTable = MyRelation.ChildTable;
427 //cRel.ExtendedProperties = cRel.ExtendedProperties;
428 //cRel.Nested = MyRelation.Nested;
429 //cRel.ParentColumns = MyRelation.ParentColumns;
430 //cRel.ParentTable = MyRelation.ParentTable;
432 Copy.Relations.Add (cRel);
439 public DataSet GetChanges ()
441 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
445 public DataSet GetChanges (DataRowState rowStates)
447 if (!HasChanges (rowStates))
450 DataSet copySet = Clone ();
451 Hashtable addedRows = new Hashtable ();
453 IEnumerator tableEnumerator = Tables.GetEnumerator ();
456 while (tableEnumerator.MoveNext ()) {
457 origTable = (DataTable)tableEnumerator.Current;
458 copyTable = copySet.Tables[origTable.TableName];
460 // Look for relations that have this table as child
461 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
463 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
464 while (rowEnumerator.MoveNext ()) {
465 DataRow row = (DataRow)rowEnumerator.Current;
467 if (row.IsRowChanged (rowStates))
468 AddChangedRow (addedRows, copySet, copyTable, relations, row);
474 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
476 if (addedRows.ContainsKey (row)) return;
479 while (relations.MoveNext ()) {
480 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
481 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
482 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
483 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
486 DataRow newRow = copyTable.NewRow ();
487 copyTable.Rows.Add (newRow);
488 row.CopyValuesToRow (newRow);
489 newRow.XmlRowID = row.XmlRowID;
490 addedRows.Add (row,row);
495 public DataTableReader GetDataReader (DataTable[] dataTables)
497 throw new NotImplementedException ();
501 public DataTableReader GetDataReader ()
503 throw new NotImplementedException ();
507 public string GetXml ()
509 StringWriter Writer = new StringWriter ();
510 WriteXml (Writer, XmlWriteMode.IgnoreSchema);
511 return Writer.ToString ();
514 public string GetXmlSchema ()
516 StringWriter Writer = new StringWriter ();
517 WriteXmlSchema (Writer);
518 return Writer.ToString ();
522 public bool HasChanges ()
524 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
528 public bool HasChanges (DataRowState rowState)
530 if (((int)rowState & 0xffffffe0) != 0)
531 throw new ArgumentOutOfRangeException ("rowState");
533 DataTableCollection tableCollection = Tables;
535 DataRowCollection rowCollection;
538 for (int i = 0; i < tableCollection.Count; i++) {
539 table = tableCollection[i];
540 rowCollection = table.Rows;
541 for (int j = 0; j < rowCollection.Count; j++) {
542 row = rowCollection[j];
543 if ((row.RowState & rowState) != 0)
551 public void InferXmlSchema (XmlReader reader, string[] nsArray)
555 XmlDocument doc = new XmlDocument ();
557 InferXmlSchema (doc, nsArray);
560 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
562 XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
565 public void InferXmlSchema (Stream stream, string[] nsArray)
567 InferXmlSchema (new XmlTextReader (stream), nsArray);
570 public void InferXmlSchema (TextReader reader, string[] nsArray)
572 InferXmlSchema (new XmlTextReader (reader), nsArray);
575 public void InferXmlSchema (string fileName, string[] nsArray)
577 XmlTextReader reader = new XmlTextReader (fileName);
579 InferXmlSchema (reader, nsArray);
587 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
589 throw new NotImplementedException ();
593 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
595 throw new NotImplementedException ();
599 public virtual void RejectChanges ()
602 bool oldEnforceConstraints = this.EnforceConstraints;
603 this.EnforceConstraints = false;
605 for (i = 0; i < this.Tables.Count;i++)
606 this.Tables[i].RejectChanges ();
608 this.EnforceConstraints = oldEnforceConstraints;
611 public virtual void Reset ()
613 IEnumerator constraintEnumerator;
615 // first we remove all ForeignKeyConstraints (if we will not do that
616 // we will get an exception when clearing the tables).
617 for (int i = 0; i < Tables.Count; i++) {
618 ConstraintCollection cc = Tables[i].Constraints;
619 for (int j = 0; j < cc.Count; j++) {
620 if (cc[j] is ForeignKeyConstraint)
630 public void WriteXml (Stream stream)
632 XmlTextWriter writer = new XmlTextWriter (stream, null);
633 writer.Formatting = Formatting.Indented;
638 /// Writes the current data for the DataSet to the specified file.
640 /// <param name="filename">Fully qualified filename to write to</param>
641 public void WriteXml (string fileName)
643 XmlTextWriter writer = new XmlTextWriter (fileName, null);
644 writer.Formatting = Formatting.Indented;
645 writer.WriteStartDocument (true);
650 writer.WriteEndDocument ();
655 public void WriteXml (TextWriter writer)
657 XmlTextWriter xwriter = new XmlTextWriter (writer);
658 xwriter.Formatting = Formatting.Indented;
662 public void WriteXml (XmlWriter writer)
664 WriteXml (writer, XmlWriteMode.IgnoreSchema);
667 public void WriteXml (string filename, XmlWriteMode mode)
669 XmlTextWriter writer = new XmlTextWriter (filename, null);
670 writer.Formatting = Formatting.Indented;
671 writer.WriteStartDocument (true);
674 WriteXml (writer, mode);
677 writer.WriteEndDocument ();
682 public void WriteXml (Stream stream, XmlWriteMode mode)
684 XmlTextWriter writer = new XmlTextWriter (stream, null);
685 writer.Formatting = Formatting.Indented;
686 WriteXml (writer, mode);
689 public void WriteXml (TextWriter writer, XmlWriteMode mode)
691 XmlTextWriter xwriter = new XmlTextWriter (writer);
692 xwriter.Formatting = Formatting.Indented;
693 WriteXml (xwriter, mode);
696 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
698 if (mode == XmlWriteMode.DiffGram) {
700 WriteDiffGramElement(writer);
703 // It should not write when there is no content to be written
704 bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
705 for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
706 shouldOutputContent = tableCollection [n].Rows.Count > 0;
708 if (shouldOutputContent) {
709 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
711 if (mode == XmlWriteMode.WriteSchema)
712 DoWriteXmlSchema (writer);
714 WriteTables (writer, mode, Tables, DataRowVersion.Default);
715 writer.WriteEndElement ();
718 if (mode == XmlWriteMode.DiffGram) {
719 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
721 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
722 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
723 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
724 writer.WriteEndElement ();
728 if (mode == XmlWriteMode.DiffGram)
729 writer.WriteEndElement (); // diffgr:diffgram
734 public void WriteXmlSchema (Stream stream)
736 XmlTextWriter writer = new XmlTextWriter (stream, null );
737 writer.Formatting = Formatting.Indented;
738 WriteXmlSchema (writer);
741 public void WriteXmlSchema (string fileName)
743 XmlTextWriter writer = new XmlTextWriter (fileName, null);
745 writer.Formatting = Formatting.Indented;
746 writer.WriteStartDocument (true);
747 WriteXmlSchema (writer);
749 writer.WriteEndDocument ();
754 public void WriteXmlSchema (TextWriter writer)
756 XmlTextWriter xwriter = new XmlTextWriter (writer);
758 xwriter.Formatting = Formatting.Indented;
759 // xwriter.WriteStartDocument ();
760 WriteXmlSchema (xwriter);
762 // xwriter.WriteEndDocument ();
767 public void WriteXmlSchema (XmlWriter writer)
769 //Create a skeleton doc and then write the schema
770 //proper which is common to the WriteXml method in schema mode
771 DoWriteXmlSchema (writer);
774 public void ReadXmlSchema (Stream stream)
776 XmlReader reader = new XmlTextReader (stream, null);
777 ReadXmlSchema (reader);
780 public void ReadXmlSchema (string str)
782 XmlReader reader = new XmlTextReader (str);
784 ReadXmlSchema (reader);
791 public void ReadXmlSchema (TextReader treader)
793 XmlReader reader = new XmlTextReader (treader);
794 ReadXmlSchema (reader);
797 public void ReadXmlSchema (XmlReader reader)
800 new XmlSchemaDataImporter (this, reader).Process ();
802 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
803 SchemaMapper.Read (reader);
807 public XmlReadMode ReadXml (Stream stream)
809 return ReadXml (new XmlTextReader (stream));
812 public XmlReadMode ReadXml (string str)
814 XmlTextReader reader = new XmlTextReader (str);
816 return ReadXml (reader);
823 public XmlReadMode ReadXml (TextReader reader)
825 return ReadXml (new XmlTextReader (reader));
828 public XmlReadMode ReadXml (XmlReader r)
830 return ReadXml (r, XmlReadMode.Auto);
833 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
835 return ReadXml (new XmlTextReader (stream), mode);
838 public XmlReadMode ReadXml (string str, XmlReadMode mode)
840 XmlTextReader reader = new XmlTextReader (str);
842 return ReadXml (reader, mode);
849 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
851 return ReadXml (new XmlTextReader (reader), mode);
854 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
856 switch (reader.ReadState) {
857 case ReadState.EndOfFile:
858 case ReadState.Error:
859 case ReadState.Closed:
862 // Skip XML declaration and prolog
863 reader.MoveToContent();
867 XmlReadMode Result = mode;
869 // If diffgram, then read the first element as diffgram
870 if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
872 case XmlReadMode.Auto:
873 case XmlReadMode.DiffGram:
874 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
875 DiffLoader.Load (reader);
876 // (and leave rest of the reader as is)
877 return XmlReadMode.DiffGram;
878 case XmlReadMode.Fragment:
880 // (and continue to read)
884 // (and leave rest of the reader as is)
888 // If schema, then read the first element as schema
889 if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
891 case XmlReadMode.IgnoreSchema:
892 case XmlReadMode.InferSchema:
894 // (and break up read)
896 case XmlReadMode.Fragment:
897 ReadXmlSchema (reader);
898 // (and continue to read)
900 case XmlReadMode.Auto:
901 if (Tables.Count == 0) {
902 ReadXmlSchema (reader);
903 return XmlReadMode.ReadSchema;
905 // otherwise just ignore and return IgnoreSchema
907 return XmlReadMode.IgnoreSchema;
910 ReadXmlSchema (reader);
911 // (and leave rest of the reader as is)
912 return mode; // When DiffGram, return DiffGram
915 // Otherwise, read as dataset... but only when required.
916 XmlReadMode explicitReturnMode = XmlReadMode.Auto;
919 case XmlReadMode.Auto:
920 if (Tables.Count > 0)
921 goto case XmlReadMode.IgnoreSchema;
923 goto case XmlReadMode.InferSchema;
924 case XmlReadMode.InferSchema:
925 doc = new XmlDocument ();
927 doc.AppendChild (doc.ReadNode (reader));
928 reader.MoveToContent ();
929 if (doc.DocumentElement != null)
931 } while (!reader.EOF);
932 InferXmlSchema (doc, null);
933 reader = new XmlNodeReader (doc);
934 explicitReturnMode = XmlReadMode.InferSchema;
936 case XmlReadMode.ReadSchema:
937 doc = new XmlDocument ();
939 doc.AppendChild (doc.ReadNode (reader));
940 reader.MoveToContent ();
941 if (doc.DocumentElement != null)
943 } while (!reader.EOF);
944 if (doc.DocumentElement != null) {
945 XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
946 if (schema != null) {
947 ReadXmlSchema (new XmlNodeReader (schema));
948 explicitReturnMode = XmlReadMode.ReadSchema;
951 reader = new XmlNodeReader (doc);
953 case XmlReadMode.IgnoreSchema:
954 case XmlReadMode.Fragment:
961 XmlDataReader.ReadXml (this, reader, mode);
962 if (explicitReturnMode != XmlReadMode.Auto)
963 return explicitReturnMode;
964 return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
966 #endregion // Public Methods
968 #region Public Events
970 [DataCategory ("Action")]
971 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
972 public event MergeFailedEventHandler MergeFailed;
974 #endregion // Public Events
976 #region IListSource methods
977 IList IListSource.GetList ()
979 return DefaultViewManager;
982 bool IListSource.ContainsListCollection {
987 #endregion IListSource methods
989 #region ISupportInitialize methods
990 public void BeginInit ()
994 public void EndInit ()
999 #region ISerializable
1000 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1002 StringWriter sw = new StringWriter ();
1003 XmlTextWriter writer = new XmlTextWriter (sw);
1004 DoWriteXmlSchema (writer);
1006 si.AddValue ("XmlSchema", sw.ToString ());
1008 sw = new StringWriter ();
1009 writer = new XmlTextWriter (sw);
1010 WriteXml (writer, XmlWriteMode.DiffGram);
1012 si.AddValue ("XmlDiffGram", sw.ToString ());
1016 #region Protected Methods
1017 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1019 string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1020 XmlTextReader reader = new XmlTextReader (new StringReader (s));
1021 ReadXmlSchema (reader);
1024 s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1025 reader = new XmlTextReader (new StringReader (s));
1026 ReadXml (reader, XmlReadMode.DiffGram);
1031 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1036 protected virtual void ReadXmlSerializable (XmlReader reader)
1038 reader.MoveToContent ();
1039 reader.ReadStartElement ();
1040 reader.MoveToContent ();
1041 ReadXmlSchema (reader);
1042 reader.MoveToContent ();
1043 ReadXml (reader, XmlReadMode.DiffGram);
1044 reader.MoveToContent ();
1045 reader.ReadEndElement ();
1048 void IXmlSerializable.ReadXml (XmlReader reader)
1050 ReadXmlSerializable(reader);
1053 void IXmlSerializable.WriteXml (XmlWriter writer)
1055 DoWriteXmlSchema (writer);
1056 WriteXml (writer, XmlWriteMode.DiffGram);
1059 XmlSchema IXmlSerializable.GetSchema ()
1061 return BuildSchema ();
1064 protected virtual bool ShouldSerializeRelations ()
1069 protected virtual bool ShouldSerializeTables ()
1075 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1080 protected virtual void OnRemoveRelation (DataRelation relation)
1085 protected virtual void OnRemoveTable (DataTable table)
1089 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1091 if (MergeFailed != null)
1092 MergeFailed (this, e);
1096 protected internal void RaisePropertyChanging (string name)
1101 #region Private Xml Serialisation
1103 private string WriteObjectXml (object o)
1105 switch (Type.GetTypeCode (o.GetType ())) {
1106 case TypeCode.Boolean:
1107 return XmlConvert.ToString ((Boolean) o);
1109 return XmlConvert.ToString ((Byte) o);
1111 return XmlConvert.ToString ((Char) o);
1112 case TypeCode.DateTime:
1113 return XmlConvert.ToString ((DateTime) o);
1114 case TypeCode.Decimal:
1115 return XmlConvert.ToString ((Decimal) o);
1116 case TypeCode.Double:
1117 return XmlConvert.ToString ((Double) o);
1118 case TypeCode.Int16:
1119 return XmlConvert.ToString ((Int16) o);
1120 case TypeCode.Int32:
1121 return XmlConvert.ToString ((Int32) o);
1122 case TypeCode.Int64:
1123 return XmlConvert.ToString ((Int64) o);
1124 case TypeCode.SByte:
1125 return XmlConvert.ToString ((SByte) o);
1126 case TypeCode.Single:
1127 return XmlConvert.ToString ((Single) o);
1128 case TypeCode.UInt16:
1129 return XmlConvert.ToString ((UInt16) o);
1130 case TypeCode.UInt32:
1131 return XmlConvert.ToString ((UInt32) o);
1132 case TypeCode.UInt64:
1133 return XmlConvert.ToString ((UInt64) o);
1135 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1136 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1137 if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1138 return o.ToString ();
1141 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1143 //Write out each table in order, providing it is not
1144 //part of another table structure via a nested parent relationship
1145 foreach (DataTable table in tableCollection) {
1146 bool isTopLevel = true;
1148 foreach (DataRelation rel in table.ParentRelations) {
1156 WriteTable ( writer, table, mode, version);
1161 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1163 DataRow[] rows = new DataRow [table.Rows.Count];
1164 table.Rows.CopyTo (rows, 0);
1165 WriteTable (writer, rows, mode, version, true);
1168 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
1170 //The columns can be attributes, hidden, elements, or simple content
1171 //There can be 0-1 simple content cols or 0-* elements
1172 System.Collections.ArrayList atts;
1173 System.Collections.ArrayList elements;
1174 DataColumn simple = null;
1176 if (rows.Length == 0) return;
1177 DataTable table = rows[0].Table;
1178 SplitColumns (table, out atts, out elements, out simple);
1179 //sort out the namespacing
1180 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1181 int relationCount = table.ParentRelations.Count;
1182 DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1184 foreach (DataRow row in rows) {
1186 // Skip rows that is a child of any tables.
1187 switch (relationCount) {
1191 if (row.GetParentRow (oneRel) != null)
1196 for (int i = 0; i < table.ParentRelations.Count; i++)
1197 if (row.GetParentRow (table.ParentRelations [i]) != null) {
1207 if (!row.HasVersion(version) ||
1208 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1209 && version == DataRowVersion.Original))
1212 // First check are all the rows null. If they are we just write empty element
1213 bool AllNulls = true;
1214 foreach (DataColumn dc in table.Columns) {
1216 if (row [dc.ColumnName, version] != DBNull.Value) {
1222 // If all of the columns were null, we have to write empty element
1224 writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1228 WriteTableElement (writer, mode, table, row, version);
1230 foreach (DataColumn col in atts) {
1231 WriteColumnAsAttribute (writer, mode, col, row, version);
1234 if (simple != null) {
1235 writer.WriteString (WriteObjectXml (row[simple, version]));
1238 foreach (DataColumn col in elements) {
1239 WriteColumnAsElement (writer, mode, col, row, version);
1243 foreach (DataRelation relation in table.ChildRelations) {
1244 if (relation.Nested) {
1245 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1249 writer.WriteEndElement ();
1254 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1256 string colnspc = null;
1257 object rowObject = row [col, version];
1259 if (rowObject == null || rowObject == DBNull.Value)
1262 if (col.Namespace != String.Empty)
1263 colnspc = col.Namespace;
1265 //TODO check if I can get away with write element string
1266 WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1267 writer.WriteString (WriteObjectXml (rowObject));
1268 writer.WriteEndElement ();
1271 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1273 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1276 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1278 //sort out the namespacing
1279 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1281 WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1283 if (mode == XmlWriteMode.DiffGram) {
1284 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1285 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1286 string modeName = null;
1287 if (row.RowState == DataRowState.Modified)
1288 modeName = "modified";
1289 else if (row.RowState == DataRowState.Added)
1290 modeName = "inserted";
1292 if (version != DataRowVersion.Original && modeName != null)
1293 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1297 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1299 writer.WriteStartElement (prefix, name, nspc);
1302 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1305 case XmlWriteMode.WriteSchema:
1306 writer.WriteAttributeString (prefix, name, nspc);
1308 case XmlWriteMode.DiffGram:
1309 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1312 writer.WriteAttributeString (name, stringValue);
1317 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1319 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1321 if (mode == XmlWriteMode.DiffGram) {
1322 SetTableRowsID (table);
1323 WriteDiffGramElement (writer);
1326 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1328 WriteTable (writer, table, mode, DataRowVersion.Default);
1330 if (mode == XmlWriteMode.DiffGram) {
1331 writer.WriteEndElement (); //DataSet name
1332 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1334 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1335 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1336 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1337 writer.WriteEndElement ();
1340 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1344 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1346 if (ns == String.Empty)
1348 if (ns != nsmgr.DefaultNamespace) {
1349 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1350 for (int i = 1; i < int.MaxValue; i++) {
1351 string p = nsmgr.NameTable.Add ("app" + i);
1352 if (!nsmgr.HasNamespace (p)) {
1353 nsmgr.AddNamespace (p, ns);
1354 HandleExternalNamespace (p, ns, schema);
1362 XmlSchema BuildSchema ()
1364 return BuildSchema (Tables, Relations);
1367 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1369 string constraintPrefix = "";
1370 XmlSchema schema = new XmlSchema ();
1371 XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1373 if (Namespace != "") {
1374 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1375 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1376 schema.TargetNamespace = Namespace;
1377 constraintPrefix = XmlConstants.TnsPrefix + ":";
1380 // set the schema id
1381 string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1382 schema.Id = DataSetName;
1383 XmlDocument doc = new XmlDocument ();
1384 XmlAttribute attr = null;
1385 ArrayList atts = new ArrayList ();
1387 nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1388 nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1389 if (Namespace != "") {
1390 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1391 nsmgr.AddNamespace (String.Empty, Namespace);
1393 if (CheckExtendedPropertyExists ())
1394 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1397 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1399 XmlSchemaElement elem = new XmlSchemaElement ();
1400 elem.Name = XmlConvert.EncodeName (DataSetName);
1402 // Add namespaces used in DataSet components (tables, columns, ...)
1403 foreach (DataTable dt in Tables) {
1404 foreach (DataColumn col in dt.Columns)
1405 CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1406 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1409 // Attributes for DataSet element
1411 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1412 attr.Value = "true";
1415 attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1416 attr.Value = locale.Name;
1419 elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1421 AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1423 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1424 elem.SchemaType = complex;
1426 XmlSchemaChoice choice = new XmlSchemaChoice ();
1427 complex.Particle = choice;
1428 choice.MaxOccursString = XmlConstants.Unbounded;
1430 //Write out schema for each table in order
1431 foreach (DataTable table in tables) {
1432 bool isTopLevel = true;
1433 foreach (DataRelation rel in table.ParentRelations) {
1441 if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1442 XmlSchemaElement extElem = new XmlSchemaElement ();
1443 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1444 choice.Items.Add (extElem);
1447 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1451 schema.Items.Add (elem);
1453 AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1454 foreach (string prefix in nsmgr) {
1455 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1456 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1457 schema.Namespaces.Add (prefix, ns);
1462 private bool CheckExtendedPropertyExists ()
1464 if (ExtendedProperties.Count > 0)
1466 foreach (DataTable dt in Tables) {
1467 if (dt.ExtendedProperties.Count > 0)
1469 foreach (DataColumn col in dt.Columns)
1470 if (col.ExtendedProperties.Count > 0)
1472 foreach (Constraint c in dt.Constraints)
1473 if (c.ExtendedProperties.Count > 0)
1476 foreach (DataRelation rel in Relations)
1477 if (rel.ExtendedProperties.Count > 0)
1482 // Add all constraints in all tables to the schema.
1483 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
1485 // first add all unique constraints.
1486 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1487 // Add all foriegn key constraints.
1488 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1491 // Add unique constaraints to the schema.
1492 // return hashtable with the names of all XmlSchemaUnique elements we created.
1493 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1495 Hashtable uniqueNames = new Hashtable();
1496 foreach (DataTable table in tables) {
1498 foreach (Constraint constraint in table.Constraints) {
1500 if (constraint is UniqueConstraint) {
1501 ArrayList attrs = new ArrayList ();
1502 XmlAttribute attrib;
1503 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1504 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1506 // if column of the constraint is hidden do not write the constraint.
1507 bool isHidden = false;
1508 foreach (DataColumn column in uqConst.Columns) {
1509 if (column.ColumnMapping == MappingType.Hidden) {
1518 // if constaraint name do not exist in the hashtable we can use it.
1519 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1520 uniq.Name = uqConst.ConstraintName;
1522 // generate new constraint name for the XmlSchemaUnique element.
1524 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1525 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1526 attrib.Value = uqConst.ConstraintName;
1529 if (uqConst.IsPrimaryKey) {
1530 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1531 attrib.Value = "true";
1535 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1537 uniq.Selector = new XmlSchemaXPath();
1538 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1539 XmlSchemaXPath field;
1540 foreach (DataColumn column in uqConst.Columns) {
1541 field = new XmlSchemaXPath();
1542 field.XPath = constraintPrefix+column.ColumnName;
1543 uniq.Fields.Add(field);
1546 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1548 elem.Constraints.Add (uniq);
1549 uniqueNames.Add (uniq.Name, null);
1556 // Add the foriegn keys to the schema.
1557 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1559 if (relations == null) return;
1561 foreach (DataRelation rel in relations) {
1563 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1566 bool isHidden = false;
1567 foreach (DataColumn col in rel.ParentColumns) {
1568 if (col.ColumnMapping == MappingType.Hidden) {
1573 foreach (DataColumn col in rel.ChildColumns) {
1574 if (col.ColumnMapping == MappingType.Hidden) {
1582 ArrayList attrs = new ArrayList ();
1583 XmlAttribute attrib;
1584 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1585 keyRef.Name = rel.RelationName;
1586 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1587 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1589 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1590 // first try to find the concatenated name. If we didn't find it - use constraint name.
1591 if (uniqueNames.ContainsKey (concatName)) {
1592 keyRef.Refer = new XmlQualifiedName(concatName);
1595 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1599 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1600 attrib.Value = "true";
1604 keyRef.Selector = new XmlSchemaXPath();
1605 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1606 XmlSchemaXPath field;
1607 foreach (DataColumn column in rel.ChildColumns) {
1608 field = new XmlSchemaXPath();
1609 field.XPath = constraintPrefix+column.ColumnName;
1610 keyRef.Fields.Add(field);
1613 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1614 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1616 elem.Constraints.Add (keyRef);
1620 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1626 SplitColumns (table, out atts, out elements, out simple);
1628 XmlSchemaElement elem = new XmlSchemaElement ();
1629 elem.Name = table.TableName;
1631 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1632 elem.SchemaType = complex;
1634 XmlSchemaObjectCollection schemaAttributes = null;
1636 if (simple != null) {
1637 // add simpleContent
1638 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1639 complex.ContentModel = simpleContent;
1641 // add column name attribute
1642 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1643 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1644 xlmAttrs[0].Value = simple.ColumnName;
1646 // add ordinal attribute
1647 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1648 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1649 simpleContent.UnhandledAttributes = xlmAttrs;
1653 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1654 simpleContent.Content = extension;
1655 extension.BaseTypeName = MapType (simple.DataType);
1656 schemaAttributes = extension.Attributes;
1658 schemaAttributes = complex.Attributes;
1659 //A sequence of element types or a simple content node
1661 XmlSchemaSequence seq = new XmlSchemaSequence ();
1663 foreach (DataColumn col in elements) {
1665 // Add element for the column.
1666 XmlSchemaElement colElem = new XmlSchemaElement ();
1667 ArrayList xattrs = new ArrayList();
1669 colElem.Name = col.ColumnName;
1671 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1672 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1673 xattr.Value = col.Caption;
1677 if (col.AutoIncrement == true) {
1678 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1679 xattr.Value = "true";
1683 if (col.AutoIncrementSeed != 0) {
1684 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1685 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1689 if (col.DefaultValue.ToString () != String.Empty)
1690 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1692 if (col.MaxLength < 0)
1693 colElem.SchemaTypeName = MapType (col.DataType);
1695 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1696 && col.DataType != typeof (char)) {
1697 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1698 xattr.Value = col.DataType.AssemblyQualifiedName;
1702 if (col.AllowDBNull) {
1703 colElem.MinOccurs = 0;
1706 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1707 // XmlConstants.Ordinal,
1708 // XmlConstants.MsdataNamespace,
1709 // col.Ordinal.ToString ());
1711 // Write SimpleType if column have MaxLength
1712 if (col.MaxLength > -1) {
1713 colElem.SchemaType = GetTableSimpleType (doc, col);
1716 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1717 AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1718 seq.Items.Add (colElem);
1721 foreach (DataRelation rel in table.ChildRelations) {
1723 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1724 XmlSchemaElement el = new XmlSchemaElement ();
1725 el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1727 XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1729 el.MaxOccursString = "unbounded";
1730 XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1732 el.SchemaType = null;
1733 el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1734 schemaToAdd.Items.Add (ct);
1740 if (seq.Items.Count > 0)
1741 complex.Particle = seq;
1744 //Then a list of attributes
1745 foreach (DataColumn col in atts) {
1746 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1747 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1748 att.Name = col.ColumnName;
1749 if (col.Namespace != String.Empty) {
1750 att.Form = XmlSchemaForm.Qualified;
1751 string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1752 att.Name = prefix + ":" + col.ColumnName;
1753 // FIXME: Handle prefix mapping correctly.
1754 schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1756 att.SchemaTypeName = MapType (col.DataType);
1757 // FIXME: what happens if extended properties are set on attribute columns??
1758 schemaAttributes.Add (att);
1761 AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1766 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1768 ArrayList attList = new ArrayList ();
1769 XmlAttribute xmlAttr;
1771 if (xsobj.UnhandledAttributes != null)
1772 attList.AddRange (xsobj.UnhandledAttributes);
1774 // add extended properties to xs:element
1775 foreach (DictionaryEntry de in props) {
1776 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1777 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1778 attList.Add (xmlAttr);
1780 if (attList.Count > 0)
1781 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1784 private string SafeNS (string ns)
1786 return ns != null ? ns : String.Empty;
1789 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1791 foreach (XmlSchemaExternal ext in schema.Includes) {
1792 XmlSchemaImport imp = ext as XmlSchemaImport;
1793 if (imp != null && imp.Namespace == ns)
1794 return; // nothing to do
1796 XmlSchemaImport i = new XmlSchemaImport ();
1798 i.SchemaLocation = "_" + prefix + ".xsd";
1799 schema.Includes.Add (i);
1802 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1805 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1808 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1809 restriction.BaseTypeName = MapType (col.DataType);
1812 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1813 max.Value = XmlConvert.ToString (col.MaxLength);
1814 restriction.Facets.Add (max);
1816 simple.Content = restriction;
1820 private void DoWriteXmlSchema (XmlWriter writer)
1822 BuildSchema ().Write (writer);
1826 /// Helper function to split columns into attributes elements and simple
1829 private void SplitColumns (DataTable table,
1831 out ArrayList elements,
1832 out DataColumn simple)
1834 //The columns can be attributes, hidden, elements, or simple content
1835 //There can be 0-1 simple content cols or 0-* elements
1836 atts = new System.Collections.ArrayList ();
1837 elements = new System.Collections.ArrayList ();
1840 //Sort out the columns
1841 foreach (DataColumn col in table.Columns) {
1842 switch (col.ColumnMapping) {
1843 case MappingType.Attribute:
1846 case MappingType.Element:
1849 case MappingType.SimpleContent:
1850 if (simple != null) {
1851 throw new System.InvalidOperationException ("There may only be one simple content element");
1856 //ignore Hidden elements
1862 private void WriteDiffGramElement(XmlWriter writer)
1864 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1865 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1868 private void SetRowsID()
1870 foreach (DataTable Table in Tables)
1871 SetTableRowsID (Table);
1874 private void SetTableRowsID (DataTable Table)
1877 foreach (DataRow Row in Table.Rows) {
1878 Row.XmlRowID = dataRowID;
1884 private XmlQualifiedName MapType (Type type)
1886 switch (Type.GetTypeCode (type)) {
1887 case TypeCode.String: return XmlConstants.QnString;
1888 case TypeCode.Int16: return XmlConstants.QnShort;
1889 case TypeCode.Int32: return XmlConstants.QnInt;
1890 case TypeCode.Int64: return XmlConstants.QnLong;
1891 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1892 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1893 //case TypeCode.Char: return XmlConstants.QnChar;
1894 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1895 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1896 case TypeCode.Double: return XmlConstants.QnDouble;
1897 case TypeCode.SByte: return XmlConstants.QnSbyte;
1898 case TypeCode.Single: return XmlConstants.QnFloat;
1899 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1900 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1901 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1904 if (typeof (TimeSpan) == type)
1905 return XmlConstants.QnDuration;
1906 else if (typeof (System.Uri) == type)
1907 return XmlConstants.QnUri;
1908 else if (typeof (byte[]) == type)
1909 return XmlConstants.QnBase64Binary;
1910 else if (typeof (XmlQualifiedName) == type)
1911 return XmlConstants.QnXmlQualifiedName;
1913 return XmlConstants.QnString;
1916 #endregion //Private Xml Serialisation