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 using System.Collections;
19 using System.ComponentModel;
20 using System.Globalization;
21 using System.Threading;
23 using System.Runtime.Serialization;
25 using System.Xml.Schema;
26 using System.Xml.Serialization;
27 using System.Data.Common;
29 namespace System.Data {
32 [DefaultProperty ("DataSetName")]
34 public class DataSet : MarshalByValueComponent, IListSource,
35 ISupportInitialize, ISerializable, IXmlSerializable
37 private string dataSetName;
38 private string _namespace = "";
39 private string prefix;
40 private bool caseSensitive;
41 private bool enforceConstraints = true;
42 private DataTableCollection tableCollection;
43 private DataRelationCollection relationCollection;
44 private PropertyCollection properties;
45 private DataViewManager defaultView;
46 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
47 internal XmlDataDocument _xmlDataDocument = null;
51 public DataSet () : this ("NewDataSet")
55 public DataSet (string name)
58 tableCollection = new DataTableCollection (this);
59 relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
60 properties = new PropertyCollection ();
61 this.prefix = String.Empty;
63 this.Locale = CultureInfo.CurrentCulture;
67 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
69 throw new NotImplementedException ();
72 #endregion // Constructors
74 #region Public Properties
76 [DataCategory ("Data")]
77 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
78 [DefaultValue (false)]
79 public bool CaseSensitive {
84 caseSensitive = value;
86 foreach (DataTable table in Tables) {
87 foreach (Constraint c in table.Constraints)
88 c.AssertConstraint ();
94 [DataCategory ("Data")]
95 [DataSysDescription ("The name of this DataSet.")]
97 public string DataSetName {
98 get { return dataSetName; }
99 set { dataSetName = value; }
102 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
104 public DataViewManager DefaultViewManager {
106 if (defaultView == null)
107 defaultView = new DataViewManager (this);
112 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
113 [DefaultValue (true)]
114 public bool EnforceConstraints {
115 get { return enforceConstraints; }
117 if (value != enforceConstraints) {
118 enforceConstraints = value;
120 foreach (DataTable table in Tables) {
121 // first assert all unique constraints
122 foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
123 uc.AssertConstraint ();
124 // then assert all foreign keys
125 foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
126 fk.AssertConstraint ();
134 [DataCategory ("Data")]
135 [DataSysDescription ("The collection that holds custom user information.")]
136 public PropertyCollection ExtendedProperties {
137 get { return properties; }
141 [DataSysDescription ("Indicates that the DataSet has errors.")]
142 public bool HasErrors {
145 for (int i = 0; i < Tables.Count; i++) {
146 if (Tables[i].HasErrors)
153 [DataCategory ("Data")]
154 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
155 public CultureInfo Locale {
160 if (locale == null || !locale.Equals (value)) {
161 // TODO: check if the new locale is valid
162 // TODO: update locale of all tables
168 public void Merge (DataRow[] rows)
170 Merge (rows, false, MissingSchemaAction.Add);
173 public void Merge (DataSet dataSet)
175 Merge (dataSet, false, MissingSchemaAction.Add);
178 public void Merge (DataTable table)
180 Merge (table, false, MissingSchemaAction.Add);
183 public void Merge (DataSet dataSet, bool preserveChanges)
185 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
189 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
192 throw new ArgumentNullException ("rows");
193 if (!IsLegalSchemaAction (missingSchemaAction))
194 throw new ArgumentOutOfRangeException ("missingSchemaAction");
196 MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
200 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
203 throw new ArgumentNullException ("dataSet");
204 if (!IsLegalSchemaAction (missingSchemaAction))
205 throw new ArgumentOutOfRangeException ("missingSchemaAction");
207 MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
211 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
214 throw new ArgumentNullException ("table");
215 if (!IsLegalSchemaAction (missingSchemaAction))
216 throw new ArgumentOutOfRangeException ("missingSchemaAction");
218 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
221 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
223 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
224 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
229 [DataCategory ("Data")]
230 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
232 public string Namespace {
233 get { return _namespace; }
235 //TODO - trigger an event if this happens?
236 if (value != this._namespace)
237 RaisePropertyChanging ("Namespace");
242 [DataCategory ("Data")]
243 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
245 public string Prefix {
246 get { return prefix; }
248 // Prefix cannot contain any special characters other than '_' and ':'
249 for (int i = 0; i < value.Length; i++) {
250 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
251 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
256 value = string.Empty;
258 if (value != this.prefix)
259 RaisePropertyChanging ("Prefix");
264 [DataCategory ("Data")]
265 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
266 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
267 public DataRelationCollection Relations {
269 return relationCollection;
274 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
275 public override ISite Site {
278 throw new NotImplementedException ();
283 throw new NotImplementedException ();
287 [DataCategory ("Data")]
288 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
289 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
290 public DataTableCollection Tables {
291 get { return tableCollection; }
294 #endregion // Public Properties
296 #region Public Methods
299 public void AcceptChanges ()
301 foreach (DataTable tempTable in tableCollection)
302 tempTable.AcceptChanges ();
307 if (_xmlDataDocument != null)
308 throw new NotSupportedException ("Clear function on dataset and datatable is not supported on XmlDataDocument.");
309 for (int t = 0; t < tableCollection.Count; t++) {
310 tableCollection[t].Clear ();
314 public virtual DataSet Clone ()
316 DataSet Copy = new DataSet ();
317 CopyProperties (Copy);
319 foreach (DataTable Table in Tables) {
320 Copy.Tables.Add (Table.Clone ());
323 //Copy Relationships between tables after existance of tables
324 //and setting properties correctly
325 CopyRelations (Copy);
330 // Copies both the structure and data for this DataSet.
331 public DataSet Copy ()
333 DataSet Copy = new DataSet ();
334 CopyProperties (Copy);
336 // Copy DatSet's tables
337 foreach (DataTable Table in Tables)
338 Copy.Tables.Add (Table.Copy ());
340 //Copy Relationships between tables after existance of tables
341 //and setting properties correctly
342 CopyRelations (Copy);
347 private void CopyProperties (DataSet Copy)
349 Copy.CaseSensitive = CaseSensitive;
350 //Copy.Container = Container
351 Copy.DataSetName = DataSetName;
352 //Copy.DefaultViewManager
354 Copy.EnforceConstraints = EnforceConstraints;
355 if(ExtendedProperties.Count > 0) {
356 // Cannot copy extended properties directly as the property does not have a set accessor
357 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
358 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
359 for (int i=0; i < ExtendedProperties.Count; i++)
360 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
362 Copy.Locale = Locale;
363 Copy.Namespace = Namespace;
364 Copy.Prefix = Prefix;
365 //Copy.Site = Site; // FIXME : Not sure of this.
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) {
378 string pTable = MyRelation.ParentTable.TableName;
379 string cTable = MyRelation.ChildTable.TableName;
380 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
381 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
384 foreach (DataColumn DC in MyRelation.ParentColumns) {
385 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
391 foreach (DataColumn DC in MyRelation.ChildColumns) {
392 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
396 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
397 //cRel.ChildColumns = MyRelation.ChildColumns;
398 //cRel.ChildTable = MyRelation.ChildTable;
399 //cRel.ExtendedProperties = cRel.ExtendedProperties;
400 //cRel.Nested = MyRelation.Nested;
401 //cRel.ParentColumns = MyRelation.ParentColumns;
402 //cRel.ParentTable = MyRelation.ParentTable;
404 Copy.Relations.Add (cRel);
411 public DataSet GetChanges ()
413 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
417 public DataSet GetChanges (DataRowState rowStates)
419 if (!HasChanges (rowStates))
422 DataSet copySet = Clone ();
423 Hashtable addedRows = new Hashtable ();
425 IEnumerator tableEnumerator = Tables.GetEnumerator ();
428 while (tableEnumerator.MoveNext ()) {
429 origTable = (DataTable)tableEnumerator.Current;
430 copyTable = copySet.Tables[origTable.TableName];
432 // Look for relations that have this table as child
433 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
435 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
436 while (rowEnumerator.MoveNext ()) {
437 DataRow row = (DataRow)rowEnumerator.Current;
439 if (row.IsRowChanged (rowStates))
440 AddChangedRow (addedRows, copySet, copyTable, relations, row);
446 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
448 if (addedRows.ContainsKey (row)) return;
451 while (relations.MoveNext ()) {
452 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
453 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
454 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
455 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
458 DataRow newRow = copyTable.NewRow ();
459 copyTable.Rows.Add (newRow);
460 row.CopyValuesToRow (newRow);
461 newRow.XmlRowID = row.XmlRowID;
462 addedRows.Add (row,row);
467 public DataTableReader GetDataReader (DataTable[] dataTables)
469 throw new NotImplementedException ();
473 public DataTableReader GetDataReader ()
475 throw new NotImplementedException ();
479 public string GetXml ()
481 StringWriter Writer = new StringWriter ();
483 // Sending false for not printing the Processing instruction
484 WriteXml (Writer, XmlWriteMode.IgnoreSchema);
485 return Writer.ToString ();
488 public string GetXmlSchema ()
490 StringWriter Writer = new StringWriter ();
491 WriteXmlSchema (Writer);
492 return Writer.ToString ();
496 public bool HasChanges ()
498 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
502 public bool HasChanges (DataRowState rowState)
504 if (((int)rowState & 0xffffffe0) != 0)
505 throw new ArgumentOutOfRangeException ("rowState");
507 DataTableCollection tableCollection = Tables;
509 DataRowCollection rowCollection;
512 for (int i = 0; i < tableCollection.Count; i++) {
513 table = tableCollection[i];
514 rowCollection = table.Rows;
515 for (int j = 0; j < rowCollection.Count; j++) {
516 row = rowCollection[j];
517 if ((row.RowState & rowState) != 0)
525 [MonoTODO ("Consider ignored namespace array")]
526 public void InferXmlSchema (XmlReader reader, string[] nsArray)
528 XmlDataLoader Loader = new XmlDataLoader (this);
529 Loader.LoadData (reader, XmlReadMode.InferSchema);
532 public void InferXmlSchema (Stream stream, string[] nsArray)
534 InferXmlSchema (new XmlTextReader (stream), nsArray);
537 public void InferXmlSchema (TextReader reader, string[] nsArray)
539 InferXmlSchema (new XmlTextReader (reader), nsArray);
542 public void InferXmlSchema (string fileName, string[] nsArray)
544 XmlTextReader reader = new XmlTextReader (fileName);
546 InferXmlSchema (reader, nsArray);
554 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
556 throw new NotImplementedException ();
560 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
562 throw new NotImplementedException ();
566 public virtual void RejectChanges ()
569 bool oldEnforceConstraints = this.EnforceConstraints;
570 this.EnforceConstraints = false;
572 for (i = 0; i < this.Tables.Count;i++)
573 this.Tables[i].RejectChanges ();
575 this.EnforceConstraints = oldEnforceConstraints;
578 public virtual void Reset ()
580 IEnumerator constraintEnumerator;
582 // first we remove all ForeignKeyConstraints (if we will not do that
583 // we will get an exception when clearing the tables).
584 for (int i = 0; i < Tables.Count; i++) {
585 ConstraintCollection cc = Tables[i].Constraints;
586 for (int j = 0; j < cc.Count; j++) {
587 if (cc[j] is ForeignKeyConstraint)
597 public void WriteXml (Stream stream)
599 XmlTextWriter writer = new XmlTextWriter (stream, null);
600 writer.Formatting = Formatting.Indented;
605 /// Writes the current data for the DataSet to the specified file.
607 /// <param name="filename">Fully qualified filename to write to</param>
608 public void WriteXml (string fileName)
610 XmlTextWriter writer = new XmlTextWriter (fileName, null);
611 writer.Formatting = Formatting.Indented;
612 writer.WriteStartDocument (true);
617 writer.WriteEndDocument ();
622 public void WriteXml (TextWriter writer)
624 XmlTextWriter xwriter = new XmlTextWriter (writer);
625 xwriter.Formatting = Formatting.Indented;
629 public void WriteXml (XmlWriter writer)
631 WriteXml (writer, XmlWriteMode.IgnoreSchema);
634 public void WriteXml (string filename, XmlWriteMode mode)
636 XmlTextWriter writer = new XmlTextWriter (filename, null);
637 writer.Formatting = Formatting.Indented;
638 writer.WriteStartDocument (true);
641 WriteXml (writer, mode);
644 writer.WriteEndDocument ();
649 public void WriteXml (Stream stream, XmlWriteMode mode)
651 XmlTextWriter writer = new XmlTextWriter (stream, null);
652 writer.Formatting = Formatting.Indented;
653 WriteXml (writer, mode);
656 public void WriteXml (TextWriter writer, XmlWriteMode mode)
658 XmlTextWriter xwriter = new XmlTextWriter (writer);
659 xwriter.Formatting = Formatting.Indented;
660 WriteXml (xwriter, mode);
663 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
665 if (mode == XmlWriteMode.DiffGram) {
667 WriteDiffGramElement(writer);
670 string ns = Namespace == null ? String.Empty : Namespace;
672 WriteStartElement (writer, mode, ns, Prefix, XmlConvert.EncodeName (DataSetName));
674 if (mode == XmlWriteMode.WriteSchema) {
675 DoWriteXmlSchema (writer);
678 WriteTables (writer, mode, Tables, DataRowVersion.Default);
679 if (mode == XmlWriteMode.DiffGram) {
680 writer.WriteEndElement (); //DataSet name
681 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
683 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
684 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
685 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
686 writer.WriteEndElement ();
689 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
692 public void WriteXmlSchema (Stream stream)
694 XmlTextWriter writer = new XmlTextWriter (stream, null );
695 writer.Formatting = Formatting.Indented;
696 WriteXmlSchema (writer);
699 public void WriteXmlSchema (string fileName)
701 XmlTextWriter writer = new XmlTextWriter (fileName, null);
702 writer.Formatting = Formatting.Indented;
703 writer.WriteStartDocument (true);
705 WriteXmlSchema (writer);
708 writer.WriteEndDocument ();
713 public void WriteXmlSchema (TextWriter writer)
715 XmlTextWriter xwriter = new XmlTextWriter (writer);
716 xwriter.Formatting = Formatting.Indented;
717 WriteXmlSchema (xwriter);
720 public void WriteXmlSchema (XmlWriter writer)
722 //Create a skeleton doc and then write the schema
723 //proper which is common to the WriteXml method in schema mode
724 DoWriteXmlSchema (writer);
727 public void ReadXmlSchema (Stream stream)
729 XmlReader reader = new XmlTextReader (stream, null);
730 ReadXmlSchema (reader);
733 public void ReadXmlSchema (string str)
735 XmlReader reader = new XmlTextReader (str);
737 ReadXmlSchema (reader);
744 public void ReadXmlSchema (TextReader treader)
746 XmlReader reader = new XmlTextReader (treader);
747 ReadXmlSchema (reader);
750 public void ReadXmlSchema (XmlReader reader)
753 new XmlSchemaDataImporter (this, reader);
755 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
756 SchemaMapper.Read (reader);
760 public XmlReadMode ReadXml (Stream stream)
762 return ReadXml (new XmlTextReader (stream));
765 public XmlReadMode ReadXml (string str)
767 XmlTextReader reader = new XmlTextReader (str);
769 return ReadXml (reader);
776 public XmlReadMode ReadXml (TextReader reader)
778 return ReadXml (new XmlTextReader (reader));
781 public XmlReadMode ReadXml (XmlReader r)
783 return ReadXml (r, XmlReadMode.Auto);
786 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
788 return ReadXml (new XmlTextReader (stream), mode);
791 public XmlReadMode ReadXml (string str, XmlReadMode mode)
793 XmlTextReader reader = new XmlTextReader (str);
795 return ReadXml (reader, mode);
802 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
804 return ReadXml (new XmlTextReader (reader), mode);
808 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
810 switch (reader.ReadState) {
811 case ReadState.EndOfFile:
812 case ReadState.Error:
813 case ReadState.Closed:
816 // Skip XML declaration and prolog
817 reader.MoveToContent();
821 XmlReadMode Result = mode;
823 // If diffgram, then read the first element as diffgram
824 if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
826 case XmlReadMode.Auto:
827 case XmlReadMode.DiffGram:
828 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
829 DiffLoader.Load (reader);
830 // (and leave rest of the reader as is)
831 return XmlReadMode.DiffGram;
832 case XmlReadMode.Fragment:
834 // (and continue to read)
838 // (and leave rest of the reader as is)
842 // If schema, then read the first element as schema
843 if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
845 case XmlReadMode.IgnoreSchema:
846 case XmlReadMode.InferSchema:
848 // (and break up read)
850 case XmlReadMode.Fragment:
851 ReadXmlSchema (reader);
852 // (and continue to read)
854 case XmlReadMode.Auto:
855 if (Tables.Count == 0) {
856 ReadXmlSchema (reader);
857 return XmlReadMode.ReadSchema;
859 // otherwise just ignore and return IgnoreSchema
861 return XmlReadMode.IgnoreSchema;
864 ReadXmlSchema (reader);
865 // (and leave rest of the reader as is)
866 return mode; // When DiffGram, return DiffGram
869 // Otherwise, read as dataset... but only when required.
871 case XmlReadMode.Auto:
872 case XmlReadMode.InferSchema:
873 case XmlReadMode.Fragment:
879 XmlDataLoader Loader = new XmlDataLoader (this);
880 return Loader.LoadData (reader, mode);
882 #endregion // Public Methods
884 #region Public Events
886 [DataCategory ("Action")]
887 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
888 public event MergeFailedEventHandler MergeFailed;
890 #endregion // Public Events
898 #endregion Destructors
900 #region IListSource methods
901 IList IListSource.GetList ()
903 return DefaultViewManager;
906 bool IListSource.ContainsListCollection {
911 #endregion IListSource methods
913 #region ISupportInitialize methods
914 public void BeginInit ()
918 public void EndInit ()
923 #region ISerializable
924 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
926 throw new NotImplementedException ();
930 #region Protected Methods
931 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
933 string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
934 if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
938 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
943 protected virtual void ReadXmlSerializable (XmlReader reader)
945 ReadXml (reader, XmlReadMode.DiffGram); // FIXME
948 void IXmlSerializable.ReadXml (XmlReader reader)
951 ReadXmlSerializable(reader);
953 // the XmlSerializationReader does this lines!!!
954 //reader.MoveToContent ();
955 //reader.ReadEndElement (); // </DataSet>
958 void IXmlSerializable.WriteXml (XmlWriter writer)
960 DoWriteXmlSchema (writer);
961 WriteXml (writer, XmlWriteMode.DiffGram);
964 protected virtual bool ShouldSerializeRelations ()
969 protected virtual bool ShouldSerializeTables ()
975 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
980 protected virtual void OnRemoveRelation (DataRelation relation)
985 protected virtual void OnRemoveTable (DataTable table)
989 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
991 if (MergeFailed != null)
992 MergeFailed (this, e);
996 protected internal void RaisePropertyChanging (string name)
1001 #region Private Xml Serialisation
1003 private string WriteObjectXml (object o)
1005 switch (Type.GetTypeCode (o.GetType ())) {
1006 case TypeCode.Boolean:
1007 return XmlConvert.ToString ((Boolean) o);
1009 return XmlConvert.ToString ((Byte) o);
1011 return XmlConvert.ToString ((Char) o);
1012 case TypeCode.DateTime:
1013 return XmlConvert.ToString ((DateTime) o);
1014 case TypeCode.Decimal:
1015 return XmlConvert.ToString ((Decimal) o);
1016 case TypeCode.Double:
1017 return XmlConvert.ToString ((Double) o);
1018 case TypeCode.Int16:
1019 return XmlConvert.ToString ((Int16) o);
1020 case TypeCode.Int32:
1021 return XmlConvert.ToString ((Int32) o);
1022 case TypeCode.Int64:
1023 return XmlConvert.ToString ((Int64) o);
1024 case TypeCode.SByte:
1025 return XmlConvert.ToString ((SByte) o);
1026 case TypeCode.Single:
1027 return XmlConvert.ToString ((Single) o);
1028 case TypeCode.UInt16:
1029 return XmlConvert.ToString ((UInt16) o);
1030 case TypeCode.UInt32:
1031 return XmlConvert.ToString ((UInt32) o);
1032 case TypeCode.UInt64:
1033 return XmlConvert.ToString ((UInt64) o);
1035 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1036 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1037 if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1038 return o.ToString ();
1041 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1043 //Write out each table in order, providing it is not
1044 //part of another table structure via a nested parent relationship
1045 foreach (DataTable table in tableCollection) {
1046 bool isTopLevel = true;
1047 foreach (DataRelation rel in table.ParentRelations) {
1055 WriteTable ( writer, table, mode, version);
1060 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1062 DataRow[] rows = new DataRow [table.Rows.Count];
1063 table.Rows.CopyTo (rows, 0);
1064 WriteTable (writer, rows, mode, version);
1067 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1069 //The columns can be attributes, hidden, elements, or simple content
1070 //There can be 0-1 simple content cols or 0-* elements
1071 System.Collections.ArrayList atts;
1072 System.Collections.ArrayList elements;
1073 DataColumn simple = null;
1075 if (rows.Length == 0) return;
1076 DataTable table = rows[0].Table;
1077 SplitColumns (table, out atts, out elements, out simple);
1078 //sort out the namespacing
1079 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1081 foreach (DataRow row in rows) {
1082 if (!row.HasVersion(version) ||
1083 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1084 && version == DataRowVersion.Original))
1087 // First check are all the rows null. If they are we just write empty element
1088 bool AllNulls = true;
1089 foreach (DataColumn dc in table.Columns) {
1091 if (row [dc.ColumnName, version] != DBNull.Value) {
1097 // If all of the columns were null, we have to write empty element
1099 writer.WriteElementString (table.TableName, "");
1103 WriteTableElement (writer, mode, table, row, version);
1105 foreach (DataColumn col in atts) {
1106 WriteColumnAsAttribute (writer, mode, col, row, version);
1109 if (simple != null) {
1110 writer.WriteString (WriteObjectXml (row[simple, version]));
1113 foreach (DataColumn col in elements) {
1114 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1118 foreach (DataRelation relation in table.ChildRelations) {
1119 if (relation.Nested) {
1120 WriteTable (writer, row.GetChildRows (relation), mode, version);
1124 writer.WriteEndElement ();
1129 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1131 string colnspc = nspc;
1132 object rowObject = row [col, version];
1134 if (rowObject == null || rowObject == DBNull.Value)
1137 if (col.Namespace != null) {
1138 colnspc = col.Namespace;
1141 //TODO check if I can get away with write element string
1142 WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1143 writer.WriteString (WriteObjectXml (rowObject));
1144 writer.WriteEndElement ();
1147 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1149 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1152 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1154 //sort out the namespacing
1155 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1157 WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1159 if (mode == XmlWriteMode.DiffGram) {
1160 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1161 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
1162 string modeName = null;
1163 if (row.RowState == DataRowState.Modified)
1164 modeName = "modified";
1165 else if (row.RowState == DataRowState.Added)
1166 modeName = "inserted";
1168 if (version != DataRowVersion.Original && modeName != null)
1169 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1173 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1175 writer.WriteStartElement (prefix, name, nspc);
1178 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1181 case XmlWriteMode.WriteSchema:
1182 writer.WriteAttributeString (prefix, name, nspc);
1184 case XmlWriteMode.DiffGram:
1185 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1188 writer.WriteAttributeString (name, stringValue);
1193 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1195 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1197 if (mode == XmlWriteMode.DiffGram) {
1198 SetTableRowsID (table);
1199 WriteDiffGramElement (writer);
1202 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1204 WriteTable (writer, table, mode, DataRowVersion.Default);
1206 if (mode == XmlWriteMode.DiffGram) {
1207 writer.WriteEndElement (); //DataSet name
1208 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1210 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1211 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1212 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1213 writer.WriteEndElement ();
1216 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1219 XmlSchema IXmlSerializable.GetSchema ()
1221 return BuildSchema ();
1224 XmlSchema BuildSchema ()
1226 return BuildSchema (Tables, Relations);
1229 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1231 string constraintPrefix = "";
1232 XmlSchema schema = new XmlSchema ();
1234 schema.Namespaces.Add("xs", XmlSchema.Namespace);
1235 schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1237 if (Namespace != "" && Namespace != null) {
1238 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1239 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1240 schema.TargetNamespace = Namespace;
1241 schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
1242 constraintPrefix = XmlConstants.TnsPrefix + ":";
1245 // set the schema id
1246 schema.Id = DataSetName;
1247 XmlDocument doc = new XmlDocument ();
1248 XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
1249 xmlnsAttr.Value = Namespace;
1251 schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
1253 XmlSchemaElement elem = new XmlSchemaElement ();
1254 elem.Name = XmlConvert.EncodeName (DataSetName);
1256 XmlAttribute[] atts = new XmlAttribute [2];
1257 atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1258 atts[0].Value = "true";
1260 atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1261 atts[1].Value = locale.Name;
1263 elem.UnhandledAttributes = atts;
1265 schema.Items.Add (elem);
1267 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1268 elem.SchemaType = complex;
1270 XmlSchemaChoice choice = new XmlSchemaChoice ();
1271 complex.Particle = choice;
1272 choice.MaxOccursString = XmlConstants.Unbounded;
1274 //Write out schema for each table in order
1275 foreach (DataTable table in tables) {
1276 bool isTopLevel = true;
1277 foreach (DataRelation rel in table.ParentRelations) {
1285 choice.Items.Add (GetTableSchema (doc, table));
1289 AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1293 // Add all constraints in all tables to the schema.
1294 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations)
1296 // first add all unique constraints.
1297 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1298 // Add all foriegn key constraints.
1299 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1302 // Add unique constaraints to the schema.
1303 // return hashtable with the names of all XmlSchemaUnique elements we created.
1304 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1306 XmlDocument doc = new XmlDocument();
1307 Hashtable uniqueNames = new Hashtable();
1308 foreach (DataTable table in tables) {
1310 foreach (Constraint constaint in table.Constraints) {
1312 if (constaint is UniqueConstraint) {
1313 ArrayList attrs = new ArrayList ();
1314 XmlAttribute attrib;
1315 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1316 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1318 // if column of the constraint is hidden do not write the constraint.
1319 bool isHidden = false;
1320 foreach (DataColumn column in uqConst.Columns) {
1321 if (column.ColumnMapping == MappingType.Hidden) {
1330 // if constaraint name do not exist in the hashtable we can use it.
1331 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1332 uniq.Name = uqConst.ConstraintName;
1334 // generate new constraint name for the XmlSchemaUnique element.
1336 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1337 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1338 attrib.Value = uqConst.ConstraintName;
1341 if (uqConst.IsPrimaryKey) {
1342 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1343 attrib.Value = "true";
1347 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1349 uniq.Selector = new XmlSchemaXPath();
1350 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1351 XmlSchemaXPath field;
1352 foreach (DataColumn column in uqConst.Columns) {
1353 field = new XmlSchemaXPath();
1354 field.XPath = constraintPrefix+column.ColumnName;
1355 uniq.Fields.Add(field);
1358 elem.Constraints.Add (uniq);
1359 uniqueNames.Add (uniq.Name, null);
1366 // Add the foriegn keys to the schema.
1367 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1369 if (relations == null) return;
1371 XmlDocument doc = new XmlDocument();
1372 foreach (DataRelation rel in relations) {
1374 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1377 ArrayList attrs = new ArrayList ();
1378 XmlAttribute attrib;
1379 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1380 keyRef.Name = rel.RelationName;
1381 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1382 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1384 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1385 // first try to find the concatenated name. If we didn't find it - use constraint name.
1386 if (uniqueNames.ContainsKey (concatName)) {
1387 keyRef.Refer = new XmlQualifiedName(concatName);
1390 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1394 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1395 attrib.Value = "true";
1399 keyRef.Selector = new XmlSchemaXPath();
1400 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1401 XmlSchemaXPath field;
1402 foreach (DataColumn column in rel.ChildColumns) {
1403 field = new XmlSchemaXPath();
1404 field.XPath = constraintPrefix+column.ColumnName;
1405 keyRef.Fields.Add(field);
1407 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1408 elem.Constraints.Add (keyRef);
1412 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1418 SplitColumns (table, out atts, out elements, out simple);
1420 XmlSchemaElement elem = new XmlSchemaElement ();
1421 elem.Name = table.TableName;
1423 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1424 elem.SchemaType = complex;
1426 //TODO - what about the simple content?
1427 if (simple != null) {
1428 // add simpleContent
1429 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1430 complex.ContentModel = simpleContent;
1432 // add column name attribute
1433 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1434 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1435 xlmAttrs[0].Value = simple.ColumnName;
1437 // add ordinal attribute
1438 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1439 xlmAttrs[1].Value = simple.Ordinal.ToString();
1440 simpleContent.UnhandledAttributes = xlmAttrs;
1444 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1445 simpleContent.Content = extension;
1446 extension.BaseTypeName = MapType (simple.DataType);
1450 //A sequence of element types or a simple content node
1452 XmlSchemaSequence seq = new XmlSchemaSequence ();
1453 complex.Particle = seq;
1455 foreach (DataColumn col in elements) {
1457 // Add element for the column.
1458 XmlSchemaElement colElem = new XmlSchemaElement ();
1459 ArrayList xattrs = new ArrayList();
1461 colElem.Name = col.ColumnName;
1463 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1464 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1465 xattr.Value = col.Caption;
1469 if (col.AutoIncrement == true) {
1470 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1471 xattr.Value = "true";
1475 if (col.AutoIncrementSeed != 0) {
1476 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1477 xattr.Value = col.AutoIncrementSeed.ToString();
1481 if (col.DefaultValue.ToString () != String.Empty)
1482 colElem.DefaultValue = col.DefaultValue.ToString ();
1484 if (col.MaxLength < 0)
1485 colElem.SchemaTypeName = MapType (col.DataType);
1487 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1488 && col.DataType != typeof (char)) {
1489 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1490 xattr.Value = col.DataType.ToString();
1494 if (col.AllowDBNull) {
1495 colElem.MinOccurs = 0;
1498 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1499 // XmlConstants.Ordinal,
1500 // XmlConstants.MsdataNamespace,
1501 // col.Ordinal.ToString ());
1503 // Write SimpleType if column have MaxLength
1504 if (col.MaxLength > -1) {
1505 colElem.SchemaType = GetTableSimpleType (doc, col);
1508 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1509 seq.Items.Add (colElem);
1512 foreach (DataRelation rel in table.ChildRelations) {
1514 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1519 //Then a list of attributes
1520 foreach (DataColumn col in atts) {
1521 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1522 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1523 att.Name = col.ColumnName;
1524 att.Form = XmlSchemaForm.Unqualified;
1525 att.SchemaTypeName = MapType (col.DataType);
1526 complex.Attributes.Add (att);
1532 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1535 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1538 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1539 restriction.BaseTypeName = MapType (col.DataType);
1542 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1543 max.Value = XmlConvert.ToString (col.MaxLength);
1544 restriction.Facets.Add (max);
1546 simple.Content = restriction;
1550 private void DoWriteXmlSchema (XmlWriter writer)
1552 BuildSchema ().Write (writer);
1556 /// Helper function to split columns into attributes elements and simple
1559 private void SplitColumns (DataTable table,
1561 out ArrayList elements,
1562 out DataColumn simple)
1564 //The columns can be attributes, hidden, elements, or simple content
1565 //There can be 0-1 simple content cols or 0-* elements
1566 atts = new System.Collections.ArrayList ();
1567 elements = new System.Collections.ArrayList ();
1570 //Sort out the columns
1571 foreach (DataColumn col in table.Columns) {
1572 switch (col.ColumnMapping) {
1573 case MappingType.Attribute:
1576 case MappingType.Element:
1579 case MappingType.SimpleContent:
1580 if (simple != null) {
1581 throw new System.InvalidOperationException ("There may only be one simple content element");
1586 //ignore Hidden elements
1592 private void WriteDiffGramElement(XmlWriter writer)
1594 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1595 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1598 private void SetRowsID()
1600 foreach (DataTable Table in Tables)
1601 SetTableRowsID (Table);
1604 private void SetTableRowsID (DataTable Table)
1607 foreach (DataRow Row in Table.Rows) {
1608 Row.XmlRowID = dataRowID;
1614 private XmlQualifiedName MapType (Type type)
1616 switch (Type.GetTypeCode (type)) {
1617 case TypeCode.String: return XmlConstants.QnString;
1618 case TypeCode.Int16: return XmlConstants.QnShort;
1619 case TypeCode.Int32: return XmlConstants.QnInt;
1620 case TypeCode.Int64: return XmlConstants.QnLong;
1621 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1622 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1623 //case TypeCode.Char: return XmlConstants.QnChar;
1624 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1625 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1626 case TypeCode.Double: return XmlConstants.QnDouble;
1627 case TypeCode.SByte: return XmlConstants.QnSbyte;
1628 case TypeCode.Single: return XmlConstants.QnFloat;
1629 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1630 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1631 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1634 if (typeof (TimeSpan) == type)
1635 return XmlConstants.QnDuration;
1636 else if (typeof (System.Uri) == type)
1637 return XmlConstants.QnUri;
1638 else if (typeof (byte[]) == type)
1639 return XmlConstants.QnBase64Binary;
1640 else if (typeof (XmlQualifiedName) == type)
1641 return XmlConstants.QnXmlQualifiedName;
1643 return XmlConstants.QnString;
1646 #endregion //Private Xml Serialisation