2 // System.Data/DataSet.cs
5 // Christopher Podurgiel <cpodurgiel@msn.com>
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Rodrigo Moya <rodrigo@ximian.com>
8 // Stuart Caborn <stuart.caborn@virgin.net>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
12 // (C) Ximian, Inc. 2002
13 // Copyright (C) Tim Coleman, 2002
17 using System.Collections;
18 using System.ComponentModel;
19 using System.Globalization;
20 using System.Threading;
22 using System.Runtime.Serialization;
24 using System.Xml.Schema;
25 using System.Xml.Serialization;
26 using System.Data.Common;
28 namespace System.Data {
30 /// an in-memory cache of data
34 [DefaultProperty ("DataSetName")]
36 public class DataSet : MarshalByValueComponent, IListSource,
37 ISupportInitialize, ISerializable, IXmlSerializable {
38 private string dataSetName;
39 private string _namespace = "";
40 private string prefix;
41 private bool caseSensitive;
42 private bool enforceConstraints = true;
43 private DataTableCollection tableCollection;
44 private DataRelationCollection relationCollection;
45 private PropertyCollection properties;
46 private DataViewManager defaultView;
47 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
51 public DataSet() : this ("NewDataSet") {
54 public DataSet(string name) {
56 tableCollection = new DataTableCollection (this);
57 relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
58 properties = new PropertyCollection();
59 this.prefix = String.Empty;
61 this.Locale = CultureInfo.CurrentCulture;
65 protected DataSet(SerializationInfo info, StreamingContext context) : this () {
66 throw new NotImplementedException ();
69 #endregion // Constructors
71 #region Public Properties
73 [DataCategory ("Data")]
74 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
75 [DefaultValue (false)]
76 public bool CaseSensitive {
77 get { return caseSensitive; }
79 foreach (DataTable T in Tables) {
80 if (T.VirginCaseSensitive)
81 T.CaseSensitive = value;
84 caseSensitive = value;
87 foreach (DataTable table in Tables)
89 foreach (Constraint c in table.Constraints)
96 [DataCategory ("Data")]
97 [DataSysDescription ("The name of this DataSet.")]
99 public string DataSetName {
100 get { return dataSetName; }
101 set { dataSetName = value; }
104 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
106 public DataViewManager DefaultViewManager {
108 if (defaultView == null)
109 defaultView = new DataViewManager (this);
114 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
115 [DefaultValue (true)]
116 public bool EnforceConstraints {
117 get { return enforceConstraints; }
119 if (value != enforceConstraints)
121 enforceConstraints = value;
124 foreach (DataTable table in Tables)
126 foreach (Constraint c in table.Constraints)
127 c.AssertConstraint();
135 [DataCategory ("Data")]
136 [DataSysDescription ("The collection that holds custom user information.")]
137 public PropertyCollection ExtendedProperties {
138 get { return properties; }
142 [DataSysDescription ("Indicates that the DataSet has errors.")]
143 public bool HasErrors {
146 for (int i = 0; i < Tables.Count; i++)
148 if (Tables[i].HasErrors)
155 [DataCategory ("Data")]
156 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
157 public CultureInfo Locale {
162 if (locale == null || !locale.Equals(value)) {
163 // TODO: check if the new locale is valid
164 // TODO: update locale of all tables
170 public void Merge (DataRow[] rows)
172 Merge (rows, false, MissingSchemaAction.Add);
175 public void Merge (DataSet dataSet)
177 Merge (dataSet, false, MissingSchemaAction.Add);
180 public void Merge (DataTable table)
182 Merge (table, false, MissingSchemaAction.Add);
185 public void Merge (DataSet dataSet, bool preserveChanges)
187 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
191 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
194 throw new ArgumentNullException("rows");
195 if(!IsLegalSchemaAction(missingSchemaAction))
196 throw new ArgumentOutOfRangeException("missingSchemaAction");
198 MergeManager.Merge(this, rows, preserveChanges, missingSchemaAction);
202 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
205 throw new ArgumentNullException("dataSet");
206 if(!IsLegalSchemaAction(missingSchemaAction))
207 throw new ArgumentOutOfRangeException("missingSchemaAction");
209 MergeManager.Merge(this, dataSet, preserveChanges, missingSchemaAction);
213 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
216 throw new ArgumentNullException("table");
217 if(!IsLegalSchemaAction(missingSchemaAction))
218 throw new ArgumentOutOfRangeException("missingSchemaAction");
220 MergeManager.Merge(this, table, preserveChanges, missingSchemaAction);
223 private static bool IsLegalSchemaAction(MissingSchemaAction missingSchemaAction)
225 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
226 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
231 [DataCategory ("Data")]
232 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
234 public string Namespace {
236 get { return _namespace; }
239 //TODO - trigger an event if this happens?
244 [DataCategory ("Data")]
245 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
247 public string Prefix {
249 get { return prefix; }
252 //TODO - trigger an event if this happens?
256 value = string.Empty;
259 if (value != this.prefix)
261 RaisePropertyChanging("Prefix");
267 [DataCategory ("Data")]
268 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
269 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
270 public DataRelationCollection Relations {
272 return relationCollection;
277 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
278 public override ISite Site {
281 throw new NotImplementedException ();
286 throw new NotImplementedException ();
290 [DataCategory ("Data")]
291 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
292 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
293 public DataTableCollection Tables {
294 get { return tableCollection; }
297 #endregion // Public Properties
299 #region Public Methods
302 public void AcceptChanges()
304 foreach (DataTable tempTable in tableCollection)
305 tempTable.AcceptChanges ();
310 // TODO: if currently bound to a XmlDataDocument
311 // throw a NotSupportedException
312 for (int t = 0; t < tableCollection.Count; t++) {
313 tableCollection[t].Clear ();
317 public virtual DataSet Clone()
319 DataSet Copy = new DataSet ();
320 CopyProperties (Copy);
322 foreach (DataTable Table in Tables) {
323 Copy.Tables.Add (Table.Clone ());
326 //Copy Relationships between tables after existance of tables
327 //and setting properties correctly
333 // Copies both the structure and data for this DataSet.
334 public DataSet Copy()
336 DataSet Copy = new DataSet ();
337 CopyProperties (Copy);
339 // Copy DatSet's tables
340 foreach (DataTable Table in Tables) {
341 Copy.Tables.Add (Table.Copy ());
344 //Copy Relationships between tables after existance of tables
345 //and setting properties correctly
352 private void CopyProperties (DataSet Copy)
354 Copy.CaseSensitive = CaseSensitive;
355 //Copy.Container = Container
356 Copy.DataSetName = DataSetName;
357 //Copy.DefaultViewManager
359 Copy.EnforceConstraints = EnforceConstraints;
360 //Copy.ExtendedProperties
362 //Copy.Locale = Locale;
363 Copy.Namespace = Namespace;
364 Copy.Prefix = Prefix;
370 private void CopyRelations (DataSet Copy)
373 //Creation of the relation contains some of the properties, and the constructor
374 //demands these values. instead changing the DataRelation constructor and behaviour the
375 //parameters are pre-configured and sent to the most general constructor
377 foreach (DataRelation MyRelation in this.Relations)
379 string pTable = MyRelation.ParentTable.TableName;
380 string cTable = MyRelation.ChildTable.TableName;
381 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
382 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
384 foreach(DataColumn DC in MyRelation.ParentColumns)
386 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
392 foreach(DataColumn DC in MyRelation.ChildColumns)
394 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
399 DataRelation cRel = new DataRelation(MyRelation.RelationName,P_DC,C_DC);
400 //cRel.ChildColumns = MyRelation.ChildColumns;
401 //cRel.ChildTable = MyRelation.ChildTable;
402 //cRel.ExtendedProperties = cRel.ExtendedProperties;
403 //cRel.Nested = MyRelation.Nested;
404 //cRel.ParentColumns = MyRelation.ParentColumns;
405 //cRel.ParentTable = MyRelation.ParentTable;
407 Copy.Relations.Add(cRel);
414 public DataSet GetChanges()
416 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
420 public DataSet GetChanges(DataRowState rowStates)
422 if(!HasChanges(rowStates))
425 DataSet copySet = Clone();
426 IEnumerator tableEnumerator = Tables.GetEnumerator();
429 while (tableEnumerator.MoveNext())
431 origTable = (DataTable)tableEnumerator.Current;
432 copyTable = copySet.Tables[origTable.TableName];
434 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator();
435 while (rowEnumerator.MoveNext())
437 DataRow row = (DataRow)rowEnumerator.Current;
438 if (row.IsRowChanged(rowStates))
440 DataRow newRow = copyTable.NewRow();
441 copyTable.Rows.Add(newRow);
442 row.CopyValuesToRow(newRow);
450 public string GetXml()
452 StringWriter Writer = new StringWriter ();
454 // Sending false for not printing the Processing instruction
455 WriteXml (Writer, XmlWriteMode.IgnoreSchema,false);
456 return Writer.ToString ();
459 public string GetXmlSchema()
461 StringWriter Writer = new StringWriter ();
462 WriteXmlSchema (Writer);
463 return Writer.ToString ();
467 public bool HasChanges()
469 return HasChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
473 public bool HasChanges(DataRowState rowState)
475 if(((int)rowState & 0xffffffe0) != 0)
476 throw new ArgumentOutOfRangeException("rowState");
478 DataTableCollection tableCollection = Tables;
480 DataRowCollection rowCollection;
482 for (int i = 0; i < tableCollection.Count; i++)
484 table = tableCollection[i];
485 rowCollection = table.Rows;
486 for (int j = 0; j < rowCollection.Count; j++)
488 row = rowCollection[j];
489 if((row.RowState & rowState) != 0)
498 public void InferXmlSchema(XmlReader reader, string[] nsArray)
502 public void InferXmlSchema(Stream stream, string[] nsArray)
504 InferXmlSchema (new XmlTextReader(stream), nsArray);
507 public void InferXmlSchema(TextReader reader, string[] nsArray)
509 InferXmlSchema (new XmlTextReader(reader), nsArray);
512 public void InferXmlSchema(string fileName, string[] nsArray)
514 XmlTextReader reader = new XmlTextReader(fileName);
516 InferXmlSchema (reader, nsArray);
522 public virtual void RejectChanges()
525 bool oldEnforceConstraints = this.EnforceConstraints;
526 this.EnforceConstraints = false;
527 for (i=0;(i<this.Tables.Count);i++)
529 this.Tables[i].RejectChanges();
531 this.EnforceConstraints = oldEnforceConstraints;
534 public virtual void Reset()
536 IEnumerator constraintEnumerator;
537 // first we remove all ForeignKeyConstraints (if we will not do that
538 // we will get an exception when clearing the tables).
539 for (int i = 0; i < Tables.Count; i++)
541 ConstraintCollection cc = Tables[i].Constraints;
542 for (int j = 0; j < cc.Count; j++)
544 if (cc[j] is ForeignKeyConstraint)
554 public void WriteXml(Stream stream)
556 XmlWriter writer = new XmlTextWriter(stream, null );
562 /// Writes the current data for the DataSet to the specified file.
564 /// <param name="filename">Fully qualified filename to write to</param>
565 public void WriteXml(string fileName)
567 XmlWriter writer = new XmlTextWriter(fileName, null );
574 public void WriteXml(TextWriter writer)
576 XmlWriter xwriter = new XmlTextWriter(writer );
581 public void WriteXml(XmlWriter writer)
583 WriteXml( writer, XmlWriteMode.IgnoreSchema,false);
586 public void WriteXml(Stream stream, XmlWriteMode mode,bool writePI)
588 XmlWriter writer = new XmlTextWriter(stream, null );
590 WriteXml( writer, mode, writePI );
593 public void WriteXml(string fileName, XmlWriteMode mode, bool writePI)
595 XmlWriter writer = new XmlTextWriter(fileName, null );
597 WriteXml( writer, mode, writePI );
602 public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writePI)
604 XmlWriter xwriter = new XmlTextWriter(writer);
606 WriteXml( xwriter, mode, writePI);
609 public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writePI)
611 if (writePI && (writer.WriteState == WriteState.Start))
612 writer.WriteStartDocument (true);
614 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
615 WriteStartElement( writer, mode, Namespace, Prefix, DataSetName );
617 if( mode == XmlWriteMode.WriteSchema )
619 DoWriteXmlSchema( writer );
622 //Write out each table in order, providing it is not
623 //part of another table structure via a nested parent relationship
624 foreach( DataTable table in Tables )
626 bool isTopLevel = true;
627 foreach( DataRelation rel in table.ParentRelations )
638 WriteTable( writer, table, mode );
642 writer.WriteEndElement();
645 public void WriteXmlSchema(Stream stream)
647 XmlWriter writer = new XmlTextWriter(stream, null );
649 WriteXmlSchema( writer );
652 public void WriteXmlSchema(string fileName)
654 XmlWriter writer = new XmlTextWriter( fileName, null );
656 WriteXmlSchema( writer );
659 public void WriteXmlSchema(TextWriter writer)
661 XmlWriter xwriter = new XmlTextWriter( writer );
663 WriteXmlSchema( xwriter );
666 public void WriteXmlSchema(XmlWriter writer)
668 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
669 //Create a skeleton doc and then write the schema
670 //proper which is common to the WriteXml method in schema mode
671 writer.WriteStartDocument();
673 DoWriteXmlSchema( writer );
675 writer.WriteEndDocument();
678 public void ReadXmlSchema(Stream stream)
680 XmlReader reader = new XmlTextReader( stream, null );
681 ReadXmlSchema( reader);
684 public void ReadXmlSchema(string str)
686 XmlReader reader = new XmlTextReader( str );
687 ReadXmlSchema( reader );
690 public void ReadXmlSchema(TextReader treader)
692 XmlReader reader = new XmlTextReader( treader );
693 ReadXmlSchema( reader );
696 public void ReadXmlSchema(XmlReader reader)
698 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
699 SchemaMapper.Read (reader);
702 public XmlReadMode ReadXml (Stream stream)
704 return ReadXml (new XmlTextReader (stream));
707 public XmlReadMode ReadXml (string str)
709 return ReadXml (new XmlTextReader (str));
712 public XmlReadMode ReadXml (TextReader reader)
714 return ReadXml (new XmlTextReader (reader));
717 public XmlReadMode ReadXml (XmlReader r)
719 XmlDataLoader Loader = new XmlDataLoader (this);
720 // FIXME: somekinda exception?
722 return XmlReadMode.Auto; // FIXME
725 * If document is diffgram we will use diffgram
727 if (r.LocalName == "diffgram")
728 return ReadXml (r, XmlReadMode.DiffGram);
731 * If we already have a schema, or the document
732 * contains an in-line schema, sets XmlReadMode to ReadSchema.
735 // FIXME: is this always true: "if we have tables we have to have schema also"
736 if (Tables.Count > 0)
737 return ReadXml (r, XmlReadMode.ReadSchema);
740 * If we dont have a schema yet and document
741 * contains no inline-schema mode is XmlReadMode.InferSchema
744 return ReadXml (r, XmlReadMode.InferSchema);
748 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
750 return ReadXml (new XmlTextReader (stream), mode);
753 public XmlReadMode ReadXml (string str, XmlReadMode mode)
755 return ReadXml (new XmlTextReader (str), mode);
758 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
760 return ReadXml (new XmlTextReader (reader), mode);
764 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
766 XmlReadMode Result = XmlReadMode.Auto;
768 if (mode == XmlReadMode.DiffGram) {
769 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
770 DiffLoader.Load (reader);
771 Result = XmlReadMode.DiffGram;
774 XmlDataLoader Loader = new XmlDataLoader (this);
775 Result = Loader.LoadData (reader, mode);
781 #endregion // Public Methods
783 #region Public Events
785 [DataCategory ("Action")]
786 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
787 public event MergeFailedEventHandler MergeFailed;
789 #endregion // Public Events
797 #endregion Destructors
799 #region IListSource methods
800 IList IListSource.GetList ()
802 return DefaultViewManager;
805 bool IListSource.ContainsListCollection {
810 #endregion IListSource methods
812 #region ISupportInitialize methods
813 public void BeginInit ()
817 public void EndInit ()
822 #region ISerializable
823 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
825 throw new NotImplementedException ();
829 #region Protected Methods
830 protected void GetSerializationData(SerializationInfo info, StreamingContext context)
832 string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
833 if (s != null) ReadXmlSerializable (new XmlTextReader(new StringReader(s)));
837 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable()
839 return BuildSchema ();
842 protected virtual void ReadXmlSerializable(XmlReader reader)
844 ReadXml(reader, XmlReadMode.DiffGram); // FIXME
847 void IXmlSerializable.ReadXml(XmlReader reader)
849 reader.MoveToContent ();
850 reader.ReadStartElement (); // <DataSet>
852 reader.MoveToContent ();
853 ReadXmlSchema (reader);
855 reader.MoveToContent ();
856 ReadXml(reader, XmlReadMode.IgnoreSchema);
858 reader.MoveToContent ();
859 reader.ReadEndElement (); // </DataSet>
862 void IXmlSerializable.WriteXml(XmlWriter writer)
864 DoWriteXmlSchema (writer);
865 WriteXml(writer, XmlWriteMode.IgnoreSchema, true);
868 protected virtual bool ShouldSerializeRelations ()
873 protected virtual bool ShouldSerializeTables ()
879 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
884 protected virtual void OnRemoveRelation (DataRelation relation)
889 protected virtual void OnRemoveTable (DataTable table)
893 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
895 if (MergeFailed != null)
896 MergeFailed(this, e);
900 protected internal void RaisePropertyChanging (string name)
905 #region Private Xml Serialisation
907 private string WriteObjectXml( object o ) {
908 switch (Type.GetTypeCode (o.GetType ())) {
909 case TypeCode.Boolean:
910 return XmlConvert.ToString ((Boolean) o);
912 return XmlConvert.ToString ((Byte) o);
914 return XmlConvert.ToString ((Char) o);
915 case TypeCode.DateTime:
916 return XmlConvert.ToString ((DateTime) o);
917 case TypeCode.Decimal:
918 return XmlConvert.ToString ((Decimal) o);
919 case TypeCode.Double:
920 return XmlConvert.ToString ((Double) o);
922 return XmlConvert.ToString ((Int16) o);
924 return XmlConvert.ToString ((Int32) o);
926 return XmlConvert.ToString ((Int64) o);
928 return XmlConvert.ToString ((SByte) o);
929 case TypeCode.Single:
930 return XmlConvert.ToString ((Single) o);
931 case TypeCode.UInt16:
932 return XmlConvert.ToString ((UInt16) o);
933 case TypeCode.UInt32:
934 return XmlConvert.ToString ((UInt32) o);
935 case TypeCode.UInt64:
936 return XmlConvert.ToString ((UInt64) o);
938 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
939 if (o is Guid) return XmlConvert.ToString ((Guid) o);
943 private void WriteTable( XmlWriter writer, DataTable table, XmlWriteMode mode )
945 DataRow[] rows = new DataRow [table.Rows.Count];
946 table.Rows.CopyTo (rows, 0);
947 WriteTable (writer, rows, mode);
950 private void WriteTable( XmlWriter writer, DataRow[] rows, XmlWriteMode mode )
952 //The columns can be attributes, hidden, elements, or simple content
953 //There can be 0-1 simple content cols or 0-* elements
954 System.Collections.ArrayList atts;
955 System.Collections.ArrayList elements;
956 DataColumn simple = null;
958 if (rows.Length == 0) return;
959 DataTable table = rows[0].Table;
960 SplitColumns( table, out atts, out elements, out simple );
962 foreach( DataRow row in rows )
964 //sort out the namespacing
965 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
967 // First check are all the rows null. If they are we just write empty element
968 bool AllNulls = true;
969 foreach (DataColumn dc in table.Columns) {
971 if (row [dc.ColumnName] != DBNull.Value) {
977 // If all of the columns were null, we have to write empty element
979 writer.WriteElementString (table.TableName, "");
983 WriteStartElement( writer, mode, nspc, table.Prefix, table.TableName );
985 foreach( DataColumn col in atts )
987 WriteAttributeString( writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col].ToString() );
992 writer.WriteString( WriteObjectXml(row[simple]) );
996 foreach( DataColumn col in elements )
998 string colnspc = nspc;
999 object rowObject = row [col];
1001 if (rowObject == null || rowObject == DBNull.Value)
1004 if( col.Namespace != null )
1006 colnspc = col.Namespace;
1009 //TODO check if I can get away with write element string
1010 WriteStartElement( writer, mode, colnspc, col.Prefix, col.ColumnName );
1011 writer.WriteString( WriteObjectXml(rowObject) );
1012 writer.WriteEndElement();
1016 foreach (DataRelation relation in table.ChildRelations) {
1017 if (relation.Nested) {
1018 WriteTable (writer, row.GetChildRows(relation), mode);
1022 writer.WriteEndElement();
1027 private void WriteStartElement( XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name )
1031 case XmlWriteMode.WriteSchema:
1032 if( nspc == null || nspc == "" )
1034 writer.WriteStartElement( name );
1036 else if( prefix != null )
1038 writer.WriteStartElement(prefix, name, nspc );
1042 writer.WriteStartElement( writer.LookupPrefix( nspc ), name, nspc );
1045 case XmlWriteMode.DiffGram:
1046 throw new NotImplementedException();
1048 writer.WriteStartElement(name );
1053 private void WriteAttributeString( XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue )
1057 case XmlWriteMode.WriteSchema:
1058 writer.WriteAttributeString(prefix, name, nspc );
1060 case XmlWriteMode.DiffGram:
1061 throw new NotImplementedException();
1063 writer.WriteAttributeString(name, stringValue );
1068 XmlSchema IXmlSerializable.GetSchema()
1070 return BuildSchema ();
1073 XmlSchema BuildSchema()
1075 XmlSchema schema = new XmlSchema ();
1076 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1078 XmlSchemaElement elem = new XmlSchemaElement ();
1079 elem.Name = DataSetName;
1081 XmlDocument doc = new XmlDocument ();
1083 XmlAttribute[] atts = new XmlAttribute [2];
1084 atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1085 atts[0].Value = "true";
1087 atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1088 atts[1].Value = locale.Name;
1089 elem.UnhandledAttributes = atts;
1091 schema.Items.Add (elem);
1093 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1094 elem.SchemaType = complex;
1096 XmlSchemaChoice choice = new XmlSchemaChoice ();
1097 complex.Particle = choice;
1098 choice.MaxOccursString = XmlConstants.Unbounded;
1100 //Write out schema for each table in order, providing it is not
1101 //part of another table structure via a nested parent relationship
1102 foreach( DataTable table in Tables )
1104 bool isTopLevel = true;
1105 foreach( DataRelation rel in table.ParentRelations )
1116 choice.Items.Add (GetTableSchema (doc, table));
1120 //TODO - now add in the relationships as key and unique constraints etc
1125 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1131 SplitColumns (table, out atts, out elements, out simple);
1133 XmlSchemaElement elem = new XmlSchemaElement ();
1134 elem.Name = table.TableName;
1136 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1137 elem.SchemaType = complex;
1139 //TODO - what about the simple content?
1140 if( elements.Count == 0 )
1145 //A sequence of element types or a simple content node
1147 XmlSchemaSequence seq = new XmlSchemaSequence ();
1148 complex.Particle = seq;
1150 foreach( DataColumn col in elements )
1152 //<xs:element name=ColumnName type=MappedType Ordinal=index>
1153 XmlSchemaElement colElem = new XmlSchemaElement ();
1154 colElem.Name = col.ColumnName;
1156 if (col.ColumnName != col.Caption && col.Caption != string.Empty)
1158 XmlAttribute[] xatts = new XmlAttribute[1];
1159 xatts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1160 xatts[0].Value = col.Caption;
1161 colElem.UnhandledAttributes = xatts;
1164 if (col.DefaultValue.ToString () != string.Empty)
1165 colElem.DefaultValue = col.DefaultValue.ToString ();
1167 colElem.SchemaTypeName = MapType (col.DataType);
1169 if( col.AllowDBNull )
1171 colElem.MinOccurs = 0;
1174 //writer.WriteAttributeString( XmlConstants.MsdataPrefix,
1175 // XmlConstants.Ordinal,
1176 // XmlConstants.MsdataNamespace,
1177 // col.Ordinal.ToString() );
1179 // Write SimpleType if column have MaxLength
1180 if (col.MaxLength > -1)
1182 colElem.SchemaType = GetTableSimpleType (doc, col);
1185 seq.Items.Add (colElem);
1189 //Then a list of attributes
1190 foreach( DataColumn col in atts )
1192 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1193 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1194 att.Name = col.ColumnName;
1195 att.Form = XmlSchemaForm.Unqualified;
1196 att.SchemaTypeName = MapType (col.DataType);
1197 complex.Attributes.Add (att);
1202 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1205 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1208 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1209 restriction.BaseTypeName = MapType (col.DataType);
1212 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1213 max.Value = XmlConvert.ToString (col.MaxLength);
1214 restriction.Facets.Add (max);
1219 private void DoWriteXmlSchema( XmlWriter writer )
1221 GetSchemaSerializable ().Write (writer);
1225 /// Helper function to split columns into attributes elements and simple
1228 private void SplitColumns( DataTable table,
1230 out ArrayList elements,
1231 out DataColumn simple)
1233 //The columns can be attributes, hidden, elements, or simple content
1234 //There can be 0-1 simple content cols or 0-* elements
1235 atts = new System.Collections.ArrayList();
1236 elements = new System.Collections.ArrayList();
1239 //Sort out the columns
1240 foreach( DataColumn col in table.Columns )
1242 switch( col.ColumnMapping )
1244 case MappingType.Attribute:
1247 case MappingType.Element:
1248 elements.Add( col );
1250 case MappingType.SimpleContent:
1251 if( simple != null )
1253 throw new System.InvalidOperationException( "There may only be one simple content element" );
1258 //ignore Hidden elements
1264 private XmlQualifiedName MapType (Type type)
1266 switch (Type.GetTypeCode (type))
1268 case TypeCode.String: return XmlConstants.QnString;
1269 case TypeCode.Int16: return XmlConstants.QnShort;
1270 case TypeCode.Int32: return XmlConstants.QnInt;
1271 case TypeCode.Int64: return XmlConstants.QnLong;
1272 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1273 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1274 case TypeCode.Char: return XmlConstants.QnChar;
1275 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1276 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1277 case TypeCode.Double: return XmlConstants.QnDouble;
1278 case TypeCode.SByte: return XmlConstants.QnSbyte;
1279 case TypeCode.Single: return XmlConstants.QnFloat;
1280 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1281 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1282 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1285 if (typeof (TimeSpan) == type) return XmlConstants.QnDuration;
1286 else if (typeof (System.Uri) == type) return XmlConstants.QnUri;
1287 else if (typeof (byte[]) == type) return XmlConstants.QnBase64Binary;
1288 else if (typeof (XmlQualifiedName) == type) return XmlConstants.QnXmlQualifiedName;
1289 else return XmlConstants.QnString;
1292 #endregion //Private Xml Serialisation