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, 2003
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 {
31 [DefaultProperty ("DataSetName")]
33 public class DataSet : MarshalByValueComponent, IListSource,
34 ISupportInitialize, ISerializable, IXmlSerializable
36 private string dataSetName;
37 private string _namespace = "";
38 private string prefix;
39 private bool caseSensitive;
40 private bool enforceConstraints = true;
41 private DataTableCollection tableCollection;
42 private DataRelationCollection relationCollection;
43 private PropertyCollection properties;
44 private DataViewManager defaultView;
45 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
49 public DataSet () : this ("NewDataSet")
53 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 ()
67 throw new NotImplementedException ();
70 #endregion // Constructors
72 #region Public Properties
74 [DataCategory ("Data")]
75 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
76 [DefaultValue (false)]
77 public bool CaseSensitive {
82 foreach (DataTable T in Tables) {
83 if (T.VirginCaseSensitive)
84 T.CaseSensitive = value;
87 caseSensitive = value;
89 foreach (DataTable table in Tables) {
90 foreach (Constraint c in table.Constraints)
91 c.AssertConstraint ();
97 [DataCategory ("Data")]
98 [DataSysDescription ("The name of this DataSet.")]
100 public string DataSetName {
101 get { return dataSetName; }
102 set { dataSetName = value; }
105 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
107 public DataViewManager DefaultViewManager {
109 if (defaultView == null)
110 defaultView = new DataViewManager (this);
115 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
116 [DefaultValue (true)]
117 public bool EnforceConstraints {
118 get { return enforceConstraints; }
120 if (value != enforceConstraints) {
121 enforceConstraints = value;
123 foreach (DataTable table in Tables) {
124 foreach (Constraint c in table.Constraints)
125 c.AssertConstraint ();
133 [DataCategory ("Data")]
134 [DataSysDescription ("The collection that holds custom user information.")]
135 public PropertyCollection ExtendedProperties {
136 get { return properties; }
140 [DataSysDescription ("Indicates that the DataSet has errors.")]
141 public bool HasErrors {
144 for (int i = 0; i < Tables.Count; i++) {
145 if (Tables[i].HasErrors)
152 [DataCategory ("Data")]
153 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
154 public CultureInfo Locale {
159 if (locale == null || !locale.Equals (value)) {
160 // TODO: check if the new locale is valid
161 // TODO: update locale of all tables
167 public void Merge (DataRow[] rows)
169 Merge (rows, false, MissingSchemaAction.Add);
172 public void Merge (DataSet dataSet)
174 Merge (dataSet, false, MissingSchemaAction.Add);
177 public void Merge (DataTable table)
179 Merge (table, false, MissingSchemaAction.Add);
182 public void Merge (DataSet dataSet, bool preserveChanges)
184 Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
188 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
191 throw new ArgumentNullException ("rows");
192 if (!IsLegalSchemaAction (missingSchemaAction))
193 throw new ArgumentOutOfRangeException ("missingSchemaAction");
195 MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
199 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
202 throw new ArgumentNullException ("dataSet");
203 if (!IsLegalSchemaAction (missingSchemaAction))
204 throw new ArgumentOutOfRangeException ("missingSchemaAction");
206 MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
210 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
213 throw new ArgumentNullException ("table");
214 if (!IsLegalSchemaAction (missingSchemaAction))
215 throw new ArgumentOutOfRangeException ("missingSchemaAction");
217 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
220 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
222 if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
223 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
228 [DataCategory ("Data")]
229 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
231 public string Namespace {
233 get { return _namespace; }
236 //TODO - trigger an event if this happens?
241 [DataCategory ("Data")]
242 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
244 public string Prefix {
246 get { return prefix; }
249 //TODO - trigger an event if this happens?
252 value = string.Empty;
254 if (value != this.prefix)
255 RaisePropertyChanging ("Prefix");
260 [DataCategory ("Data")]
261 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
262 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
263 public DataRelationCollection Relations {
265 return relationCollection;
270 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
271 public override ISite Site {
274 throw new NotImplementedException ();
279 throw new NotImplementedException ();
283 [DataCategory ("Data")]
284 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
285 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
286 public DataTableCollection Tables {
287 get { return tableCollection; }
290 #endregion // Public Properties
292 #region Public Methods
295 public void AcceptChanges ()
297 foreach (DataTable tempTable in tableCollection)
298 tempTable.AcceptChanges ();
303 // TODO: if currently bound to a XmlDataDocument
304 // throw a NotSupportedException
305 for (int t = 0; t < tableCollection.Count; t++) {
306 tableCollection[t].Clear ();
310 public virtual DataSet Clone ()
312 DataSet Copy = new DataSet ();
313 CopyProperties (Copy);
315 foreach (DataTable Table in Tables) {
316 Copy.Tables.Add (Table.Clone ());
319 //Copy Relationships between tables after existance of tables
320 //and setting properties correctly
321 CopyRelations (Copy);
326 // Copies both the structure and data for this DataSet.
327 public DataSet Copy ()
329 DataSet Copy = new DataSet ();
330 CopyProperties (Copy);
332 // Copy DatSet's tables
333 foreach (DataTable Table in Tables)
334 Copy.Tables.Add (Table.Copy ());
336 //Copy Relationships between tables after existance of tables
337 //and setting properties correctly
338 CopyRelations (Copy);
344 private void CopyProperties (DataSet Copy)
346 Copy.CaseSensitive = CaseSensitive;
347 //Copy.Container = Container
348 Copy.DataSetName = DataSetName;
349 //Copy.DefaultViewManager
351 Copy.EnforceConstraints = EnforceConstraints;
352 //Copy.ExtendedProperties
354 //Copy.Locale = Locale;
355 Copy.Namespace = Namespace;
356 Copy.Prefix = Prefix;
362 private void CopyRelations (DataSet Copy)
365 //Creation of the relation contains some of the properties, and the constructor
366 //demands these values. instead changing the DataRelation constructor and behaviour the
367 //parameters are pre-configured and sent to the most general constructor
369 foreach (DataRelation MyRelation in this.Relations) {
370 string pTable = MyRelation.ParentTable.TableName;
371 string cTable = MyRelation.ChildTable.TableName;
372 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length];
373 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
376 foreach (DataColumn DC in MyRelation.ParentColumns) {
377 P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
383 foreach (DataColumn DC in MyRelation.ChildColumns) {
384 C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
388 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
389 //cRel.ChildColumns = MyRelation.ChildColumns;
390 //cRel.ChildTable = MyRelation.ChildTable;
391 //cRel.ExtendedProperties = cRel.ExtendedProperties;
392 //cRel.Nested = MyRelation.Nested;
393 //cRel.ParentColumns = MyRelation.ParentColumns;
394 //cRel.ParentTable = MyRelation.ParentTable;
396 Copy.Relations.Add (cRel);
403 public DataSet GetChanges ()
405 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
409 public DataSet GetChanges (DataRowState rowStates)
411 if (!HasChanges (rowStates))
414 DataSet copySet = Clone ();
415 Hashtable addedRows = new Hashtable ();
417 IEnumerator tableEnumerator = Tables.GetEnumerator ();
420 while (tableEnumerator.MoveNext ()) {
421 origTable = (DataTable)tableEnumerator.Current;
422 copyTable = copySet.Tables[origTable.TableName];
424 // Look for relations that have this table as child
425 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
427 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
428 while (rowEnumerator.MoveNext ()) {
429 DataRow row = (DataRow)rowEnumerator.Current;
431 if (row.IsRowChanged (rowStates))
432 AddChangedRow (addedRows, copySet, copyTable, relations, row);
438 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
440 if (addedRows.ContainsKey (row)) return;
443 while (relations.MoveNext ()) {
444 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
445 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
446 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
447 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
450 DataRow newRow = copyTable.NewRow ();
451 copyTable.Rows.Add (newRow);
452 row.CopyValuesToRow (newRow);
453 newRow.XmlRowID = row.XmlRowID;
454 addedRows.Add (row,row);
459 public DataTableReader GetDataReader (DataTable[] dataTables)
461 throw new NotImplementedException ();
465 public DataTableReader GetDataReader ()
467 throw new NotImplementedException ();
471 public string GetXml ()
473 StringWriter Writer = new StringWriter ();
475 // Sending false for not printing the Processing instruction
476 WriteXml (Writer, XmlWriteMode.IgnoreSchema, false);
477 return Writer.ToString ();
480 public string GetXmlSchema ()
482 StringWriter Writer = new StringWriter ();
483 WriteXmlSchema (Writer);
484 return Writer.ToString ();
488 public bool HasChanges ()
490 return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
494 public bool HasChanges (DataRowState rowState)
496 if (((int)rowState & 0xffffffe0) != 0)
497 throw new ArgumentOutOfRangeException ("rowState");
499 DataTableCollection tableCollection = Tables;
501 DataRowCollection rowCollection;
504 for (int i = 0; i < tableCollection.Count; i++) {
505 table = tableCollection[i];
506 rowCollection = table.Rows;
507 for (int j = 0; j < rowCollection.Count; j++) {
508 row = rowCollection[j];
509 if ((row.RowState & rowState) != 0)
518 public void InferXmlSchema (XmlReader reader, string[] nsArray)
522 public void InferXmlSchema (Stream stream, string[] nsArray)
524 InferXmlSchema (new XmlTextReader (stream), nsArray);
527 public void InferXmlSchema (TextReader reader, string[] nsArray)
529 InferXmlSchema (new XmlTextReader (reader), nsArray);
532 public void InferXmlSchema (string fileName, string[] nsArray)
534 XmlTextReader reader = new XmlTextReader (fileName);
536 InferXmlSchema (reader, nsArray);
544 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
546 throw new NotImplementedException ();
550 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
552 throw new NotImplementedException ();
556 public virtual void RejectChanges ()
559 bool oldEnforceConstraints = this.EnforceConstraints;
560 this.EnforceConstraints = false;
562 for (i = 0; i < this.Tables.Count;i++)
563 this.Tables[i].RejectChanges ();
565 this.EnforceConstraints = oldEnforceConstraints;
568 public virtual void Reset ()
570 IEnumerator constraintEnumerator;
572 // first we remove all ForeignKeyConstraints (if we will not do that
573 // we will get an exception when clearing the tables).
574 for (int i = 0; i < Tables.Count; i++) {
575 ConstraintCollection cc = Tables[i].Constraints;
576 for (int j = 0; j < cc.Count; j++) {
577 if (cc[j] is ForeignKeyConstraint)
587 public void WriteXml (Stream stream)
589 XmlTextWriter writer = new XmlTextWriter (stream, null);
590 writer.Formatting = Formatting.Indented;
595 /// Writes the current data for the DataSet to the specified file.
597 /// <param name="filename">Fully qualified filename to write to</param>
598 public void WriteXml (string fileName)
600 XmlTextWriter writer = new XmlTextWriter (fileName, null);
601 writer.Formatting = Formatting.Indented;
607 public void WriteXml (TextWriter writer)
609 XmlTextWriter xwriter = new XmlTextWriter (writer);
610 xwriter.Formatting = Formatting.Indented;
614 public void WriteXml (XmlWriter writer)
616 WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
619 public void WriteXml (string filename, XmlWriteMode mode)
621 XmlTextWriter writer = new XmlTextWriter (filename, null);
622 writer.Formatting = Formatting.Indented;
623 WriteXml (writer, mode, true);
626 public void WriteXml (Stream stream, XmlWriteMode mode)
628 XmlTextWriter writer = new XmlTextWriter (stream, null);
629 writer.Formatting = Formatting.Indented;
630 WriteXml (writer, mode, true);
633 public void WriteXml (TextWriter writer, XmlWriteMode mode)
635 XmlTextWriter xwriter = new XmlTextWriter (writer);
636 xwriter.Formatting = Formatting.Indented;
637 WriteXml (xwriter, mode, true);
640 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
642 WriteXml (writer, mode, true);
645 internal void WriteXml (Stream stream, XmlWriteMode mode, bool writePI)
647 XmlTextWriter writer = new XmlTextWriter (stream, null);
648 writer.Formatting = Formatting.Indented;
649 WriteXml (writer, mode, writePI);
652 internal void WriteXml (string fileName, XmlWriteMode mode, bool writePI)
654 XmlTextWriter writer = new XmlTextWriter (fileName, null);
655 writer.Formatting = Formatting.Indented;
656 WriteXml (writer, mode, writePI);
661 internal void WriteXml (TextWriter writer, XmlWriteMode mode, bool writePI)
663 XmlTextWriter xwriter = new XmlTextWriter (writer);
664 xwriter.Formatting = Formatting.Indented;
665 WriteXml (xwriter, mode, writePI);
668 internal void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writePI)
670 if (writePI && (writer.WriteState == WriteState.Start))
671 writer.WriteStartDocument (true);
673 if (mode == XmlWriteMode.DiffGram) {
675 WriteDiffGramElement(writer);
678 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
680 /*********************************************************
681 * This is a patch for interoperability with ms.net. *
682 * Because in web services the .net client expects this *
683 * atrribute even if namespace is an empty string *
684 ********************************************************/
685 if (Namespace == null || Namespace.Length == 0)
686 WriteAttributeString (writer, mode, null, null, "xmlns", Namespace);
689 if (mode == XmlWriteMode.WriteSchema) {
690 DoWriteXmlSchema (writer);
693 WriteTables (writer, mode, Tables, DataRowVersion.Default);
694 if (mode == XmlWriteMode.DiffGram) {
695 writer.WriteEndElement (); //DataSet name
696 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
698 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
699 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
700 WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
701 writer.WriteEndElement ();
704 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
707 public void WriteXmlSchema (Stream stream)
709 XmlTextWriter writer = new XmlTextWriter (stream, null );
710 writer.Formatting = Formatting.Indented;
711 WriteXmlSchema (writer);
714 public void WriteXmlSchema (string fileName)
716 XmlTextWriter writer = new XmlTextWriter (fileName, null);
717 writer.Formatting = Formatting.Indented;
718 WriteXmlSchema (writer);
721 public void WriteXmlSchema (TextWriter writer)
723 XmlTextWriter xwriter = new XmlTextWriter (writer);
724 xwriter.Formatting = Formatting.Indented;
725 WriteXmlSchema (xwriter);
728 public void WriteXmlSchema (XmlWriter writer)
730 //Create a skeleton doc and then write the schema
731 //proper which is common to the WriteXml method in schema mode
732 writer.WriteStartDocument ();
733 DoWriteXmlSchema (writer);
735 writer.WriteEndDocument ();
738 public void ReadXmlSchema (Stream stream)
740 XmlReader reader = new XmlTextReader (stream, null);
741 ReadXmlSchema (reader);
744 public void ReadXmlSchema (string str)
746 XmlReader reader = new XmlTextReader (str);
747 ReadXmlSchema (reader);
750 public void ReadXmlSchema (TextReader treader)
752 XmlReader reader = new XmlTextReader (treader);
753 ReadXmlSchema (reader);
756 public void ReadXmlSchema (XmlReader reader)
758 XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
759 SchemaMapper.Read (reader);
762 public XmlReadMode ReadXml (Stream stream)
764 return ReadXml (new XmlTextReader (stream));
767 public XmlReadMode ReadXml (string str)
769 return ReadXml (new XmlTextReader (str));
772 public XmlReadMode ReadXml (TextReader reader)
774 return ReadXml (new XmlTextReader (reader));
777 public XmlReadMode ReadXml (XmlReader r)
779 XmlDataLoader Loader = new XmlDataLoader (this);
780 // FIXME: somekinda exception?
782 return XmlReadMode.Auto; // FIXME
784 // Check if the curent element is the process instruction (PI).
785 // if it is move to next element.
786 if (r.LocalName == "xml")
790 * If document is diffgram we will use diffgram
792 if (r.LocalName == "diffgram")
793 return ReadXml (r, XmlReadMode.DiffGram);
795 // Get the DataSet name.
796 string dataSetName = XmlConvert.DecodeName (r.LocalName);
797 DataSetName = dataSetName;
799 r.ReadStartElement ();
802 bool schemaRead = false;
803 // Check if the current element is the schema
804 if (r.LocalName == "schema") {
810 if (r.LocalName == "diffgram") {
811 return ReadXml (r, XmlReadMode.DiffGram);
814 // If the schema has been read we should read the rest of the document
816 ReadXml (r, XmlReadMode.IgnoreSchema, false);
817 return XmlReadMode.ReadSchema;
820 // Read with inferschema.
821 return ReadXml (r, XmlReadMode.InferSchema, false);
825 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
827 return ReadXml (new XmlTextReader (stream), mode);
830 public XmlReadMode ReadXml (string str, XmlReadMode mode)
832 return ReadXml (new XmlTextReader (str), mode);
835 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
837 return ReadXml (new XmlTextReader (reader), mode);
841 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
843 // we have to initiate the reader.
844 if (reader.ReadState == ReadState.Initial)
847 // Check if the curent element is the process instruction (PI).
848 // if it is move to next element.
849 if (reader.LocalName == "xml")
850 reader.MoveToContent();
852 XmlReadMode Result = XmlReadMode.Auto;
854 if (mode == XmlReadMode.DiffGram) {
855 if (reader.LocalName != "diffgram"){
856 reader.MoveToContent ();
857 reader.ReadStartElement (); // <DataSet>
859 reader.MoveToContent ();
860 if (reader.LocalName == "schema")
861 ReadXmlSchema (reader);
863 reader.MoveToContent ();
865 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
866 DiffLoader.Load (reader);
867 Result = XmlReadMode.DiffGram;
870 Result = ReadXml(reader, mode, true);
875 private XmlReadMode ReadXml (XmlReader r, XmlReadMode mode, bool readDataSet) {
878 string dataSetName = XmlConvert.DecodeName (r.LocalName);
879 DataSetName = dataSetName;
880 // get the Namespace of the DataSet.
881 string tmp = r.GetAttribute("xmlns");
885 r.ReadStartElement ();
889 XmlDataLoader Loader = new XmlDataLoader (this);
890 return Loader.LoadData (r, mode);
893 #endregion // Public Methods
895 #region Public Events
897 [DataCategory ("Action")]
898 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
899 public event MergeFailedEventHandler MergeFailed;
901 #endregion // Public Events
909 #endregion Destructors
911 #region IListSource methods
912 IList IListSource.GetList ()
914 return DefaultViewManager;
917 bool IListSource.ContainsListCollection {
922 #endregion IListSource methods
924 #region ISupportInitialize methods
925 public void BeginInit ()
929 public void EndInit ()
934 #region ISerializable
935 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
937 throw new NotImplementedException ();
941 #region Protected Methods
942 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
944 string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
945 if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
949 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
951 return BuildSchema ();
954 protected virtual void ReadXmlSerializable (XmlReader reader)
956 ReadXml (reader, XmlReadMode.DiffGram); // FIXME
959 void IXmlSerializable.ReadXml (XmlReader reader)
962 ReadXmlSerializable(reader);
964 // the XmlSerializationReader does this lines!!!
965 //reader.MoveToContent ();
966 //reader.ReadEndElement (); // </DataSet>
969 void IXmlSerializable.WriteXml (XmlWriter writer)
971 DoWriteXmlSchema (writer);
972 WriteXml (writer, XmlWriteMode.DiffGram, true);
975 protected virtual bool ShouldSerializeRelations ()
980 protected virtual bool ShouldSerializeTables ()
986 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
991 protected virtual void OnRemoveRelation (DataRelation relation)
996 protected virtual void OnRemoveTable (DataTable table)
1000 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1002 if (MergeFailed != null)
1003 MergeFailed (this, e);
1007 protected internal void RaisePropertyChanging (string name)
1012 #region Private Xml Serialisation
1014 private string WriteObjectXml (object o)
1016 switch (Type.GetTypeCode (o.GetType ())) {
1017 case TypeCode.Boolean:
1018 return XmlConvert.ToString ((Boolean) o);
1020 return XmlConvert.ToString ((Byte) o);
1022 return XmlConvert.ToString ((Char) o);
1023 case TypeCode.DateTime:
1024 return XmlConvert.ToString ((DateTime) o);
1025 case TypeCode.Decimal:
1026 return XmlConvert.ToString ((Decimal) o);
1027 case TypeCode.Double:
1028 return XmlConvert.ToString ((Double) o);
1029 case TypeCode.Int16:
1030 return XmlConvert.ToString ((Int16) o);
1031 case TypeCode.Int32:
1032 return XmlConvert.ToString ((Int32) o);
1033 case TypeCode.Int64:
1034 return XmlConvert.ToString ((Int64) o);
1035 case TypeCode.SByte:
1036 return XmlConvert.ToString ((SByte) o);
1037 case TypeCode.Single:
1038 return XmlConvert.ToString ((Single) o);
1039 case TypeCode.UInt16:
1040 return XmlConvert.ToString ((UInt16) o);
1041 case TypeCode.UInt32:
1042 return XmlConvert.ToString ((UInt32) o);
1043 case TypeCode.UInt64:
1044 return XmlConvert.ToString ((UInt64) o);
1046 if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1047 if (o is Guid) return XmlConvert.ToString ((Guid) o);
1048 return o.ToString ();
1051 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1053 //Write out each table in order, providing it is not
1054 //part of another table structure via a nested parent relationship
1055 foreach (DataTable table in tableCollection) {
1056 bool isTopLevel = true;
1057 foreach (DataRelation rel in table.ParentRelations) {
1065 WriteTable ( writer, table, mode, version);
1070 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1072 DataRow[] rows = new DataRow [table.Rows.Count];
1073 table.Rows.CopyTo (rows, 0);
1074 WriteTable (writer, rows, mode, version);
1077 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1079 //The columns can be attributes, hidden, elements, or simple content
1080 //There can be 0-1 simple content cols or 0-* elements
1081 System.Collections.ArrayList atts;
1082 System.Collections.ArrayList elements;
1083 DataColumn simple = null;
1085 if (rows.Length == 0) return;
1086 DataTable table = rows[0].Table;
1087 SplitColumns (table, out atts, out elements, out simple);
1088 //sort out the namespacing
1089 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1091 foreach (DataRow row in rows) {
1092 if (!row.HasVersion(version) ||
1093 (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged
1094 && version == DataRowVersion.Original))
1097 // First check are all the rows null. If they are we just write empty element
1098 bool AllNulls = true;
1099 foreach (DataColumn dc in table.Columns) {
1101 if (row [dc.ColumnName, version] != DBNull.Value) {
1107 // If all of the columns were null, we have to write empty element
1109 writer.WriteElementString (table.TableName, "");
1113 WriteTableElement (writer, mode, table, row, version);
1115 foreach (DataColumn col in atts) {
1116 WriteColumnAsAttribute (writer, mode, col, row, version);
1119 if (simple != null) {
1120 writer.WriteString (WriteObjectXml (row[simple, version]));
1123 foreach (DataColumn col in elements) {
1124 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1128 foreach (DataRelation relation in table.ChildRelations) {
1129 if (relation.Nested) {
1130 WriteTable (writer, row.GetChildRows (relation), mode, version);
1134 writer.WriteEndElement ();
1139 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1141 string colnspc = nspc;
1142 object rowObject = row [col, version];
1144 if (rowObject == null || rowObject == DBNull.Value)
1147 if (col.Namespace != null) {
1148 colnspc = col.Namespace;
1151 //TODO check if I can get away with write element string
1152 WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1153 writer.WriteString (WriteObjectXml (rowObject));
1154 writer.WriteEndElement ();
1157 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1159 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1162 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1164 //sort out the namespacing
1165 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1167 WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1169 if (mode == XmlWriteMode.DiffGram) {
1170 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1171 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
1172 string modeName = null;
1173 if (row.RowState == DataRowState.Modified)
1174 modeName = "modified";
1175 else if (row.RowState == DataRowState.Added)
1176 modeName = "inserted";
1178 if (version != DataRowVersion.Original && modeName != null)
1179 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1183 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1185 if (nspc == null || nspc == "") {
1186 writer.WriteStartElement (name);
1188 else if (prefix != null) {
1189 writer.WriteStartElement (prefix, name, nspc);
1192 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1196 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1199 case XmlWriteMode.WriteSchema:
1200 writer.WriteAttributeString (prefix, name, nspc);
1202 case XmlWriteMode.DiffGram:
1203 writer.WriteAttributeString (prefix, name, nspc,stringValue);
1206 writer.WriteAttributeString (name, stringValue);
1211 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1213 ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1215 if (mode == XmlWriteMode.DiffGram) {
1216 SetTableRowsID (table);
1217 WriteDiffGramElement (writer);
1220 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1222 WriteTable (writer, table, mode, DataRowVersion.Default);
1224 if (mode == XmlWriteMode.DiffGram) {
1225 writer.WriteEndElement (); //DataSet name
1226 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1228 DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
1229 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1230 WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1231 writer.WriteEndElement ();
1234 writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1237 XmlSchema IXmlSerializable.GetSchema ()
1239 return BuildSchema ();
1242 XmlSchema BuildSchema ()
1244 return BuildSchema (Tables, Relations);
1247 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1249 string constraintPrefix = "";
1250 XmlSchema schema = new XmlSchema ();
1252 schema.Namespaces.Add("xs", XmlSchema.Namespace);
1253 schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1255 if (Namespace != "" && Namespace != null) {
1256 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1257 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1258 schema.TargetNamespace = Namespace;
1259 schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
1260 constraintPrefix = XmlConstants.TnsPrefix + ":";
1263 // set the schema id
1264 schema.Id = DataSetName;
1265 XmlDocument doc = new XmlDocument ();
1266 XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
1267 xmlnsAttr.Value = Namespace;
1269 schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
1271 XmlSchemaElement elem = new XmlSchemaElement ();
1272 elem.Name = XmlConvert.EncodeName (DataSetName);
1274 XmlAttribute[] atts = new XmlAttribute [2];
1275 atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1276 atts[0].Value = "true";
1278 atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1279 atts[1].Value = locale.Name;
1280 elem.UnhandledAttributes = atts;
1282 schema.Items.Add (elem);
1284 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1285 elem.SchemaType = complex;
1287 XmlSchemaChoice choice = new XmlSchemaChoice ();
1288 complex.Particle = choice;
1289 choice.MaxOccursString = XmlConstants.Unbounded;
1291 //Write out schema for each table in order
1292 foreach (DataTable table in tables) {
1293 bool isTopLevel = true;
1294 foreach (DataRelation rel in table.ParentRelations) {
1302 choice.Items.Add (GetTableSchema (doc, table));
1306 AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1310 // Add all constraints in all tables to the schema.
1311 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations)
1313 // first add all unique constraints.
1314 Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1315 // Add all foriegn key constraints.
1316 AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1319 // Add unique constaraints to the schema.
1320 // return hashtable with the names of all XmlSchemaUnique elements we created.
1321 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1323 XmlDocument doc = new XmlDocument();
1324 Hashtable uniqueNames = new Hashtable();
1325 foreach (DataTable table in tables) {
1327 foreach (Constraint constaint in table.Constraints) {
1329 if (constaint is UniqueConstraint) {
1330 ArrayList attrs = new ArrayList ();
1331 XmlAttribute attrib;
1332 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1333 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1335 // if constaraint name do not exist in the hashtable we can use it.
1336 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1337 uniq.Name = uqConst.ConstraintName;
1339 // generate new constraint name for the XmlSchemaUnique element.
1341 uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1342 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1343 attrib.Value = uqConst.ConstraintName;
1346 if (uqConst.IsPrimaryKey) {
1347 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1348 attrib.Value = "true";
1352 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1354 uniq.Selector = new XmlSchemaXPath();
1355 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1356 XmlSchemaXPath field;
1357 foreach (DataColumn column in uqConst.Columns) {
1358 field = new XmlSchemaXPath();
1359 field.XPath = constraintPrefix+column.ColumnName;
1360 uniq.Fields.Add(field);
1363 elem.Constraints.Add (uniq);
1364 uniqueNames.Add (uniq.Name, null);
1371 // Add the foriegn keys to the schema.
1372 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1374 if (relations == null) return;
1376 XmlDocument doc = new XmlDocument();
1377 foreach (DataRelation rel in relations) {
1379 ArrayList attrs = new ArrayList ();
1380 XmlAttribute attrib;
1381 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1382 keyRef.Name = rel.RelationName;
1383 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1384 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1386 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1387 // first try to find the concatenated name. If we didn't find it - use constraint name.
1388 if (uniqueNames.ContainsKey (concatName)) {
1389 keyRef.Refer = new XmlQualifiedName(concatName);
1392 keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1396 attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1397 attrib.Value = "true";
1401 keyRef.Selector = new XmlSchemaXPath();
1402 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1403 XmlSchemaXPath field;
1404 foreach (DataColumn column in rel.ChildColumns) {
1405 field = new XmlSchemaXPath();
1406 field.XPath = constraintPrefix+column.ColumnName;
1407 keyRef.Fields.Add(field);
1409 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1410 elem.Constraints.Add (keyRef);
1414 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1420 SplitColumns (table, out atts, out elements, out simple);
1422 XmlSchemaElement elem = new XmlSchemaElement ();
1423 elem.Name = table.TableName;
1425 XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1426 elem.SchemaType = complex;
1428 //TODO - what about the simple content?
1429 if (elements.Count == 0) {
1432 //A sequence of element types or a simple content node
1434 XmlSchemaSequence seq = new XmlSchemaSequence ();
1435 complex.Particle = seq;
1437 foreach (DataColumn col in elements) {
1438 //<xs:element name=ColumnName type=MappedType Ordinal=index>
1439 XmlSchemaElement colElem = new XmlSchemaElement ();
1440 ArrayList xattrs = new ArrayList();
1442 colElem.Name = col.ColumnName;
1444 if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1445 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1446 xattr.Value = col.Caption;
1450 if (col.AutoIncrement == true) {
1451 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1452 xattr.Value = "true";
1456 if (col.AutoIncrementSeed != 0) {
1457 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1458 xattr.Value = col.AutoIncrementSeed.ToString();
1462 if (col.DefaultValue.ToString () != String.Empty)
1463 colElem.DefaultValue = col.DefaultValue.ToString ();
1465 if (col.MaxLength < 0)
1466 colElem.SchemaTypeName = MapType (col.DataType);
1468 if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string)
1469 && col.DataType != typeof (char)) {
1470 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1471 xattr.Value = col.DataType.ToString();
1475 if (col.AllowDBNull) {
1476 colElem.MinOccurs = 0;
1479 //writer.WriteAttributeString (XmlConstants.MsdataPrefix,
1480 // XmlConstants.Ordinal,
1481 // XmlConstants.MsdataNamespace,
1482 // col.Ordinal.ToString ());
1484 // Write SimpleType if column have MaxLength
1485 if (col.MaxLength > -1) {
1486 colElem.SchemaType = GetTableSimpleType (doc, col);
1489 colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1490 seq.Items.Add (colElem);
1493 foreach (DataRelation rel in table.ChildRelations) {
1495 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1500 //Then a list of attributes
1501 foreach (DataColumn col in atts) {
1502 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1503 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1504 att.Name = col.ColumnName;
1505 att.Form = XmlSchemaForm.Unqualified;
1506 att.SchemaTypeName = MapType (col.DataType);
1507 complex.Attributes.Add (att);
1513 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1516 XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1519 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1520 restriction.BaseTypeName = MapType (col.DataType);
1523 XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1524 max.Value = XmlConvert.ToString (col.MaxLength);
1525 restriction.Facets.Add (max);
1527 simple.Content = restriction;
1531 private void DoWriteXmlSchema (XmlWriter writer)
1533 GetSchemaSerializable ().Write (writer);
1537 /// Helper function to split columns into attributes elements and simple
1540 private void SplitColumns (DataTable table,
1542 out ArrayList elements,
1543 out DataColumn simple)
1545 //The columns can be attributes, hidden, elements, or simple content
1546 //There can be 0-1 simple content cols or 0-* elements
1547 atts = new System.Collections.ArrayList ();
1548 elements = new System.Collections.ArrayList ();
1551 //Sort out the columns
1552 foreach (DataColumn col in table.Columns) {
1553 switch (col.ColumnMapping) {
1554 case MappingType.Attribute:
1557 case MappingType.Element:
1560 case MappingType.SimpleContent:
1561 if (simple != null) {
1562 throw new System.InvalidOperationException ("There may only be one simple content element");
1567 //ignore Hidden elements
1573 private void WriteDiffGramElement(XmlWriter writer)
1575 WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1576 WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1579 private void SetRowsID()
1581 foreach (DataTable Table in Tables) {
1583 foreach (DataRow Row in Table.Rows) {
1584 Row.XmlRowID = dataRowID;
1591 private XmlQualifiedName MapType (Type type)
1593 switch (Type.GetTypeCode (type)) {
1594 case TypeCode.String: return XmlConstants.QnString;
1595 case TypeCode.Int16: return XmlConstants.QnShort;
1596 case TypeCode.Int32: return XmlConstants.QnInt;
1597 case TypeCode.Int64: return XmlConstants.QnLong;
1598 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1599 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1600 //case TypeCode.Char: return XmlConstants.QnChar;
1601 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1602 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1603 case TypeCode.Double: return XmlConstants.QnDouble;
1604 case TypeCode.SByte: return XmlConstants.QnSbyte;
1605 case TypeCode.Single: return XmlConstants.QnFloat;
1606 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1607 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1608 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1611 if (typeof (TimeSpan) == type)
1612 return XmlConstants.QnDuration;
1613 else if (typeof (System.Uri) == type)
1614 return XmlConstants.QnUri;
1615 else if (typeof (byte[]) == type)
1616 return XmlConstants.QnBase64Binary;
1617 else if (typeof (XmlQualifiedName) == type)
1618 return XmlConstants.QnXmlQualifiedName;
1620 return XmlConstants.QnString;
1623 #endregion //Private Xml Serialisation