* DataSet.cs :
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
index 756a4bafec2e7d3e417cc7bf8f2a168319c4afbc..741c6a2b74738a50f467a028ca6b225e1cc1fc52 100644 (file)
@@ -8,6 +8,7 @@
 //   Stuart Caborn <stuart.caborn@virgin.net>
 //   Tim Coleman (tim@timcoleman.com)
 //   Ville Palo <vi64pa@koti.soon.fi>
+//   Atsushi Enomoto <atsushi@ximian.com>
 //
 // (C) Ximian, Inc. 2002
 // Copyright (C) Tim Coleman, 2002, 2003
@@ -31,7 +32,8 @@ namespace System.Data {
        [DefaultProperty ("DataSetName")]
        [Serializable]
        public class DataSet : MarshalByValueComponent, IListSource, 
-               ISupportInitialize, ISerializable, IXmlSerializable {
+               ISupportInitialize, ISerializable, IXmlSerializable 
+       {
                private string dataSetName;
                private string _namespace = "";
                private string prefix;
@@ -42,10 +44,12 @@ namespace System.Data {
                private PropertyCollection properties;
                private DataViewManager defaultView;
                private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
+               internal XmlDataDocument _xmlDataDocument = null;
                
-#region Constructors
+               #region Constructors
 
-               public DataSet () : this ("NewDataSet") {               
+               public DataSet () : this ("NewDataSet") 
+               {               
                }
                
                public DataSet (string name)
@@ -60,7 +64,8 @@ namespace System.Data {
                }
 
                [MonoTODO]
-               protected DataSet (SerializationInfo info, StreamingContext context) : this () {
+               protected DataSet (SerializationInfo info, StreamingContext context) : this ()
+               {
                        throw new NotImplementedException ();
                }
 
@@ -76,11 +81,6 @@ namespace System.Data {
                                return caseSensitive;
                        } 
                        set {
-                               foreach (DataTable T in Tables) {
-                                       if (T.VirginCaseSensitive)
-                                               T.CaseSensitive = value;
-                               }
-
                                caseSensitive = value; 
                                if (!caseSensitive) {
                                        foreach (DataTable table in Tables) {
@@ -118,8 +118,12 @@ namespace System.Data {
                                        enforceConstraints = value; 
                                        if (value) {
                                                foreach (DataTable table in Tables) {
-                                                       foreach (Constraint c in table.Constraints)
-                                                               c.AssertConstraint ();
+                                                       // first assert all unique constraints
+                                                       foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
+                                                               uc.AssertConstraint ();
+                                                       // then assert all foreign keys
+                                                       foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
+                                                               fk.AssertConstraint ();
                                                }
                                        }
                                }
@@ -138,8 +142,7 @@ namespace System.Data {
                public bool HasErrors {
                        [MonoTODO]
                        get {
-                               for (int i = 0; i < Tables.Count; i++)
-                               {
+                               for (int i = 0; i < Tables.Count; i++) {
                                        if (Tables[i].HasErrors)
                                                return true;
                                }
@@ -227,12 +230,12 @@ namespace System.Data {
                [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
                [DefaultValue ("")]
                public string Namespace {
-                       [MonoTODO]
                        get { return _namespace; } 
-                       [MonoTODO]
                        set {
                                //TODO - trigger an event if this happens?
-                               _namespace = value;
+                                if (value != this._namespace)
+                                        RaisePropertyChanging ("Namespace");
+                               _namespace = value;
                        }
                }
 
@@ -240,11 +243,14 @@ namespace System.Data {
                [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
                [DefaultValue ("")]
                public string Prefix {
-                       [MonoTODO]
                        get { return prefix; } 
-                       [MonoTODO]
                        set {
-                               //TODO - trigger an event if this happens?
+                              // Prefix cannot contain any special characters other than '_' and ':'
+                               for (int i = 0; i < value.Length; i++) {
+                                       if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
+                                               throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
+                               }
+
 
                                if (value == null)
                                        value = string.Empty;
@@ -298,8 +304,8 @@ namespace System.Data {
 
                public void Clear ()
                {
-                       // TODO: if currently bound to a XmlDataDocument
-                       //       throw a NotSupportedException
+                       if (_xmlDataDocument != null)
+                               throw new NotSupportedException ("Clear function on dataset and datatable is not supported on XmlDataDocument.");
                        for (int t = 0; t < tableCollection.Count; t++) {
                                tableCollection[t].Clear ();
                        }
@@ -338,7 +344,6 @@ namespace System.Data {
                        return Copy;
                }
 
-               [MonoTODO]
                private void CopyProperties (DataSet Copy)
                {
                        Copy.CaseSensitive = CaseSensitive;
@@ -347,12 +352,17 @@ namespace System.Data {
                        //Copy.DefaultViewManager
                        //Copy.DesignMode
                        Copy.EnforceConstraints = EnforceConstraints;
-                       //Copy.ExtendedProperties 
-                       //Copy.HasErrors
-                       //Copy.Locale = Locale;
+                       if(ExtendedProperties.Count > 0) {
+                               //  Cannot copy extended properties directly as the property does not have a set accessor
+                Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
+                ExtendedProperties.Keys.CopyTo (tgtArray, 0);
+                for (int i=0; i < ExtendedProperties.Count; i++)
+                                       Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
+                       }
+            Copy.Locale = Locale;
                        Copy.Namespace = Namespace;
                        Copy.Prefix = Prefix;                   
-                       //Copy.Site = Site;
+                       //Copy.Site = Site; // FIXME : Not sure of this.
 
                }
                
@@ -364,8 +374,7 @@ namespace System.Data {
                        //demands these values. instead changing the DataRelation constructor and behaviour the
                        //parameters are pre-configured and sent to the most general constructor
 
-                       foreach (DataRelation MyRelation in this.Relations)
-                       {
+                       foreach (DataRelation MyRelation in this.Relations) {
                                string pTable = MyRelation.ParentTable.TableName;
                                string cTable = MyRelation.ChildTable.TableName;
                                DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
@@ -411,26 +420,47 @@ namespace System.Data {
                                return null;
                        
                        DataSet copySet = Clone ();
+                       Hashtable addedRows = new Hashtable ();
+
                        IEnumerator tableEnumerator = Tables.GetEnumerator ();
                        DataTable origTable;
                        DataTable copyTable;
                        while (tableEnumerator.MoveNext ()) {
                                origTable = (DataTable)tableEnumerator.Current;
                                copyTable = copySet.Tables[origTable.TableName];
+                               
+                               // Look for relations that have this table as child
+                               IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
 
                                IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
                                while (rowEnumerator.MoveNext ()) {
                                        DataRow row = (DataRow)rowEnumerator.Current;
+                                       
                                        if (row.IsRowChanged (rowStates))
-                                       {
-                                               DataRow newRow = copyTable.NewRow ();
-                                               copyTable.Rows.Add (newRow);
-                                               row.CopyValuesToRow (newRow);
-                                       }
+                                               AddChangedRow (addedRows, copySet, copyTable, relations, row);
                                }
                        }
                        return copySet;
                }
+               
+               void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
+               {
+                       if (addedRows.ContainsKey (row)) return;
+                       
+                       relations.Reset ();
+                       while (relations.MoveNext ()) {
+                               DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
+                               if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
+                               DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
+                               AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
+                       }
+               
+                       DataRow newRow = copyTable.NewRow ();
+                       copyTable.Rows.Add (newRow);
+                       row.CopyValuesToRow (newRow);
+                       newRow.XmlRowID = row.XmlRowID;
+                       addedRows.Add (row,row);
+               }
 
 #if NET_1_2
                [MonoTODO]
@@ -451,7 +481,7 @@ namespace System.Data {
                        StringWriter Writer = new StringWriter ();
 
                        // Sending false for not printing the Processing instruction
-                       WriteXml (Writer, XmlWriteMode.IgnoreSchema, false);
+                       WriteXml (Writer, XmlWriteMode.IgnoreSchema);
                        return Writer.ToString ();
                }
 
@@ -492,9 +522,11 @@ namespace System.Data {
                        return false;           
                }
 
-               [MonoTODO]
+               [MonoTODO ("Consider ignored namespace array")]
                public void InferXmlSchema (XmlReader reader, string[] nsArray)
                {
+                       XmlDataLoader Loader = new XmlDataLoader (this);
+                       Loader.LoadData (reader, XmlReadMode.InferSchema);
                }
 
                public void InferXmlSchema (Stream stream, string[] nsArray)
@@ -564,8 +596,8 @@ namespace System.Data {
 
                public void WriteXml (Stream stream)
                {
-                       XmlWriter writer = new XmlTextWriter (stream, null);
-                       
+                       XmlTextWriter writer = new XmlTextWriter (stream, null);
+                       writer.Formatting = Formatting.Indented;
                        WriteXml (writer);
                }
 
@@ -575,139 +607,121 @@ namespace System.Data {
                /// <param name="filename">Fully qualified filename to write to</param>
                public void WriteXml (string fileName)
                {
-                       XmlWriter writer = new XmlTextWriter (fileName, null);
-                       
-                       WriteXml (writer);
-                       
-                       writer.Close ();
+                       XmlTextWriter writer = new XmlTextWriter (fileName, null);
+                       writer.Formatting = Formatting.Indented;
+                       writer.WriteStartDocument (true);
+                       try {
+                               WriteXml (writer);
+                       }
+                       finally {
+                               writer.WriteEndDocument ();
+                               writer.Close ();
+                       }
                }
 
                public void WriteXml (TextWriter writer)
                {
-                       XmlWriter xwriter = new XmlTextWriter (writer);
-                       
+                       XmlTextWriter xwriter = new XmlTextWriter (writer);
+                       xwriter.Formatting = Formatting.Indented;
                        WriteXml (xwriter);
                }
 
                public void WriteXml (XmlWriter writer)
                {
-                       WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
+                       WriteXml (writer, XmlWriteMode.IgnoreSchema);
                }
 
                public void WriteXml (string filename, XmlWriteMode mode)
                {
-                       XmlWriter writer = new XmlTextWriter (filename, null);
-                       WriteXml (writer, mode, true);
+                       XmlTextWriter writer = new XmlTextWriter (filename, null);
+                       writer.Formatting = Formatting.Indented;
+                       writer.WriteStartDocument (true);
+                       
+                       try {
+                               WriteXml (writer, mode);
+                       }
+                       finally {
+                               writer.WriteEndDocument ();
+                               writer.Close ();
+                       }
                }
 
                public void WriteXml (Stream stream, XmlWriteMode mode)
                {
-                       XmlWriter writer = new XmlTextWriter (stream, null);
-
-                       WriteXml (writer, mode, true);
+                       XmlTextWriter writer = new XmlTextWriter (stream, null);
+                       writer.Formatting = Formatting.Indented;
+                       WriteXml (writer, mode);
                }
 
                public void WriteXml (TextWriter writer, XmlWriteMode mode)
                {
-                       XmlWriter xwriter = new XmlTextWriter (writer);
-                       WriteXml (xwriter, mode, true);
+                       XmlTextWriter xwriter = new XmlTextWriter (writer);
+                       xwriter.Formatting = Formatting.Indented;
+                       WriteXml (xwriter, mode);
                }
 
                public void WriteXml (XmlWriter writer, XmlWriteMode mode)
                {
-                       WriteXml (writer, mode, true);
-               }
-               
-               internal void WriteXml (Stream stream, XmlWriteMode mode, bool writePI)
-               {
-                       XmlWriter writer = new XmlTextWriter (stream, null);
-                       
-                       WriteXml (writer, mode, writePI);
-               }
-
-               internal void WriteXml (string fileName, XmlWriteMode mode, bool writePI)
-               {
-                       XmlWriter writer = new XmlTextWriter (fileName, null);
-                       
-                       WriteXml (writer, mode, writePI);
-                       
-                       writer.Close ();
-               }
-
-               internal void WriteXml (TextWriter writer, XmlWriteMode mode, bool writePI)
-               {
-                       XmlWriter xwriter = new XmlTextWriter (writer);
-                       
-                       WriteXml (xwriter, mode, writePI);
-               }
+                       if (mode == XmlWriteMode.DiffGram) {
+                               SetRowsID();
+                               WriteDiffGramElement(writer);
+                       }
 
-               internal void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writePI)
-               {
-                       if (writePI && (writer.WriteState == WriteState.Start))
-                               writer.WriteStartDocument (true);
+                       string ns = Namespace == null ? String.Empty : Namespace;
 
-                        ((XmlTextWriter)writer).Formatting = Formatting.Indented;
-                       WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
+                       WriteStartElement (writer, mode, ns, Prefix, XmlConvert.EncodeName (DataSetName));
                        
-                       if (mode == XmlWriteMode.WriteSchema)
-                       {
+                       if (mode == XmlWriteMode.WriteSchema) {
                                DoWriteXmlSchema (writer);
                        }
                        
-                       //Write out each table in order, providing it is not
-                       //part of another table structure via a nested parent relationship
-                       foreach (DataTable table in Tables)
-                       {
-                               bool isTopLevel = true;
-                               foreach (DataRelation rel in table.ParentRelations)
-                               {
-                                       if (rel.Nested)
-                                       {
-                                               isTopLevel = false;
-                                               break;
-                                       }
-                               }
-                               
-                               if (isTopLevel)
-                               {
-                                       WriteTable ( writer, table, mode);
+                       WriteTables (writer, mode, Tables, DataRowVersion.Default);
+                       if (mode == XmlWriteMode.DiffGram) {
+                               writer.WriteEndElement (); //DataSet name
+                               if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
+
+                                       DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
+                                       WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
+                                       WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
+                                       writer.WriteEndElement ();
                                }
                        }
-                       
-                       writer.WriteEndElement ();                      
+                       writer.WriteEndElement (); // DataSet name or diffgr:diffgram
                }
 
                public void WriteXmlSchema (Stream stream)
                {
-                       XmlWriter writer = new XmlTextWriter (stream, null );
-                       
+                       XmlTextWriter writer = new XmlTextWriter (stream, null );
+                       writer.Formatting = Formatting.Indented;
                        WriteXmlSchema (writer);        
                }
 
                public void WriteXmlSchema (string fileName)
                {
-                       XmlWriter writer = new XmlTextWriter (fileName, null);
-               
-                       WriteXmlSchema (writer);
+                       XmlTextWriter writer = new XmlTextWriter (fileName, null);
+                       writer.Formatting = Formatting.Indented;
+                       writer.WriteStartDocument (true);
+                       try {
+                               WriteXmlSchema (writer);
+                       }
+                       finally {
+                               writer.WriteEndDocument ();
+                               writer.Close ();
+                       }
                }
 
                public void WriteXmlSchema (TextWriter writer)
                {
-                       XmlWriter xwriter = new XmlTextWriter (writer);
-                       
+                       XmlTextWriter xwriter = new XmlTextWriter (writer);
+                       xwriter.Formatting = Formatting.Indented;
                        WriteXmlSchema (xwriter);
                }
 
                public void WriteXmlSchema (XmlWriter writer)
                {
-                        ((XmlTextWriter)writer).Formatting = Formatting.Indented;
                        //Create a skeleton doc and then write the schema 
                        //proper which is common to the WriteXml method in schema mode
-                       writer.WriteStartDocument ();
-                       
                        DoWriteXmlSchema (writer);
-                       
-                       writer.WriteEndDocument ();
                }
 
                public void ReadXmlSchema (Stream stream)
@@ -719,7 +733,12 @@ namespace System.Data {
                public void ReadXmlSchema (string str)
                {
                        XmlReader reader = new XmlTextReader (str);
-                       ReadXmlSchema (reader);
+                       try {
+                               ReadXmlSchema (reader);
+                       }
+                       finally {
+                               reader.Close ();
+                       }
                }
 
                public void ReadXmlSchema (TextReader treader)
@@ -730,8 +749,12 @@ namespace System.Data {
 
                public void ReadXmlSchema (XmlReader reader)
                {
+#if true
+                       new XmlSchemaDataImporter (this, reader);
+#else
                        XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
                        SchemaMapper.Read (reader);
+#endif
                }
 
                public XmlReadMode ReadXml (Stream stream)
@@ -741,7 +764,13 @@ namespace System.Data {
 
                public XmlReadMode ReadXml (string str)
                {
-                       return ReadXml (new XmlTextReader (str));
+                       XmlTextReader reader = new XmlTextReader (str);
+                       try {
+                               return ReadXml (reader);
+                       }
+                       finally {
+                               reader.Close ();
+                       }
                }
 
                public XmlReadMode ReadXml (TextReader reader)
@@ -751,33 +780,7 @@ namespace System.Data {
 
                public XmlReadMode ReadXml (XmlReader r)
                {
-                       XmlDataLoader Loader = new XmlDataLoader (this);
-                       // FIXME: somekinda exception?
-                       if (!r.Read ())
-                               return XmlReadMode.Auto; // FIXME
-
-                       /*\
-                        *  If document is diffgram we will use diffgram
-                       \*/
-                       if (r.LocalName == "diffgram")
-                               return ReadXml (r, XmlReadMode.DiffGram);
-
-                       /*\
-                        *  If we already have a schema, or the document 
-                        *  contains an in-line schema, sets XmlReadMode to ReadSchema.
-                       \*/
-
-                       // FIXME: is this always true: "if we have tables we have to have schema also"
-                       if (Tables.Count > 0)                           
-                               return ReadXml (r, XmlReadMode.ReadSchema);
-
-                       /*\
-                        *  If we dont have a schema yet and document 
-                        *  contains no inline-schema  mode is XmlReadMode.InferSchema
-                       \*/
-
-                       return ReadXml (r, XmlReadMode.InferSchema);
-
+                       return ReadXml (r, XmlReadMode.Auto);
                }
 
                public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
@@ -787,7 +790,13 @@ namespace System.Data {
 
                public XmlReadMode ReadXml (string str, XmlReadMode mode)
                {
-                       return ReadXml (new XmlTextReader (str), mode);
+                       XmlTextReader reader = new XmlTextReader (str);
+                       try {
+                               return ReadXml (reader, mode);
+                       }
+                       finally {
+                               reader.Close ();
+                       }
                }
 
                public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
@@ -798,21 +807,78 @@ namespace System.Data {
                [MonoTODO]
                public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
                {
-                       XmlReadMode Result = XmlReadMode.Auto;
-
-                       if (mode == XmlReadMode.DiffGram) {
-                               XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
-                               DiffLoader.Load (reader);
-                               Result =  XmlReadMode.DiffGram;
+                       switch (reader.ReadState) {
+                       case ReadState.EndOfFile:
+                       case ReadState.Error:
+                       case ReadState.Closed:
+                               return mode;
                        }
-                       else {
-                               XmlDataLoader Loader = new XmlDataLoader (this);
-                               Result = Loader.LoadData (reader, mode);
+                       // Skip XML declaration and prolog
+                       reader.MoveToContent();
+                       if (reader.EOF)
+                               return mode;
+
+                       XmlReadMode Result = mode;
+
+                       // If diffgram, then read the first element as diffgram 
+                       if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
+                               switch (mode) {
+                               case XmlReadMode.Auto:
+                               case XmlReadMode.DiffGram:
+                                       XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
+                                       DiffLoader.Load (reader);
+                                       // (and leave rest of the reader as is)
+                                       return  XmlReadMode.DiffGram;
+                               case XmlReadMode.Fragment:
+                                       reader.Skip ();
+                                       // (and continue to read)
+                                       break;
+                               default:
+                                       reader.Skip ();
+                                       // (and leave rest of the reader as is)
+                                       return mode;
+                               }
                        }
-
-                       return Result;
+                       // If schema, then read the first element as schema 
+                       if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
+                               switch (mode) {
+                               case XmlReadMode.IgnoreSchema:
+                               case XmlReadMode.InferSchema:
+                                       reader.Skip ();
+                                       // (and break up read)
+                                       return mode;
+                               case XmlReadMode.Fragment:
+                                       ReadXmlSchema (reader);
+                                       // (and continue to read)
+                                       break;
+                               case XmlReadMode.Auto:
+                                       if (Tables.Count == 0) {
+                                               ReadXmlSchema (reader);
+                                               return XmlReadMode.ReadSchema;
+                                       } else {
+                                       // otherwise just ignore and return IgnoreSchema
+                                               reader.Skip ();
+                                               return XmlReadMode.IgnoreSchema;
+                                       }
+                               default:
+                                       ReadXmlSchema (reader);
+                                       // (and leave rest of the reader as is)
+                                       return mode; // When DiffGram, return DiffGram
+                               }
+                       }
+                       // Otherwise, read as dataset... but only when required.
+                       switch (mode) {
+                       case XmlReadMode.Auto:
+                       case XmlReadMode.InferSchema:
+                       case XmlReadMode.Fragment:
+                               break;
+                       default:
+                               reader.Skip ();
+                               return mode;
+                       }
+                       XmlDataLoader Loader = new XmlDataLoader (this);
+                       return Loader.LoadData (reader, mode);
                }
-
                #endregion // Public Methods
 
                #region Public Events
@@ -871,7 +937,7 @@ namespace System.Data {
                
                protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
                {
-                       return BuildSchema ();
+                       return null;
                }
                
                protected virtual void ReadXmlSerializable (XmlReader reader)
@@ -881,14 +947,8 @@ namespace System.Data {
 
                void IXmlSerializable.ReadXml (XmlReader reader)
                {
-                       reader.MoveToContent ();
-                       reader.ReadStartElement ();     // <DataSet>
 
-                       reader.MoveToContent ();
-                       ReadXmlSchema (reader);
-
-                       reader.MoveToContent ();
-                       ReadXml (reader, XmlReadMode.IgnoreSchema);
+                       ReadXmlSerializable(reader);
                        
                        // the XmlSerializationReader does this lines!!!
                        //reader.MoveToContent ();
@@ -898,7 +958,7 @@ namespace System.Data {
                void IXmlSerializable.WriteXml (XmlWriter writer)
                {
                        DoWriteXmlSchema (writer);
-                       WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
+                       WriteXml (writer, XmlWriteMode.DiffGram);
                }
 
                protected virtual bool ShouldSerializeRelations ()
@@ -940,50 +1000,71 @@ namespace System.Data {
 
                #region Private Xml Serialisation
 
-               private string WriteObjectXml (object o) {
+               private string WriteObjectXml (object o)
+               {
                        switch (Type.GetTypeCode (o.GetType ())) {
-                       case TypeCode.Boolean:
-                               return XmlConvert.ToString ((Boolean) o);
-                       case TypeCode.Byte:
-                               return XmlConvert.ToString ((Byte) o);
-                       case TypeCode.Char:
-                               return XmlConvert.ToString ((Char) o);
-                       case TypeCode.DateTime:
-                               return XmlConvert.ToString ((DateTime) o);
-                       case TypeCode.Decimal:
-                               return XmlConvert.ToString ((Decimal) o);
-                       case TypeCode.Double:
-                               return XmlConvert.ToString ((Double) o);
-                       case TypeCode.Int16:
-                               return XmlConvert.ToString ((Int16) o);
-                       case TypeCode.Int32:
-                               return XmlConvert.ToString ((Int32) o);
-                       case TypeCode.Int64:
-                               return XmlConvert.ToString ((Int64) o);
-                       case TypeCode.SByte:
-                               return XmlConvert.ToString ((SByte) o);
-                       case TypeCode.Single:
-                               return XmlConvert.ToString ((Single) o);
-                       case TypeCode.UInt16:
-                               return XmlConvert.ToString ((UInt16) o);
-                       case TypeCode.UInt32:
-                               return XmlConvert.ToString ((UInt32) o);
-                       case TypeCode.UInt64:
-                               return XmlConvert.ToString ((UInt64) o);
+                               case TypeCode.Boolean:
+                                       return XmlConvert.ToString ((Boolean) o);
+                               case TypeCode.Byte:
+                                       return XmlConvert.ToString ((Byte) o);
+                               case TypeCode.Char:
+                                       return XmlConvert.ToString ((Char) o);
+                               case TypeCode.DateTime:
+                                       return XmlConvert.ToString ((DateTime) o);
+                               case TypeCode.Decimal:
+                                       return XmlConvert.ToString ((Decimal) o);
+                               case TypeCode.Double:
+                                       return XmlConvert.ToString ((Double) o);
+                               case TypeCode.Int16:
+                                       return XmlConvert.ToString ((Int16) o);
+                               case TypeCode.Int32:
+                                       return XmlConvert.ToString ((Int32) o);
+                               case TypeCode.Int64:
+                                       return XmlConvert.ToString ((Int64) o);
+                               case TypeCode.SByte:
+                                       return XmlConvert.ToString ((SByte) o);
+                               case TypeCode.Single:
+                                       return XmlConvert.ToString ((Single) o);
+                               case TypeCode.UInt16:
+                                       return XmlConvert.ToString ((UInt16) o);
+                               case TypeCode.UInt32:
+                                       return XmlConvert.ToString ((UInt32) o);
+                               case TypeCode.UInt64:
+                                       return XmlConvert.ToString ((UInt64) o);
                        }
                        if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
                        if (o is Guid) return XmlConvert.ToString ((Guid) o);
+                       if (o is byte[]) return Convert.ToBase64String ((byte[])o);
                        return o.ToString ();
                }
-       
-               private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode)
+               
+               private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
+               {
+                       //Write out each table in order, providing it is not
+                       //part of another table structure via a nested parent relationship
+                       foreach (DataTable table in tableCollection) {
+                               bool isTopLevel = true;
+                               foreach (DataRelation rel in table.ParentRelations) {
+                                       if (rel.Nested) {
+                                               isTopLevel = false;
+                                               break;
+                                       }
+                               }
+                               
+                               if (isTopLevel) {
+                                       WriteTable ( writer, table, mode, version);
+                               }
+                       }
+               }
+
+               private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
                {
                        DataRow[] rows = new DataRow [table.Rows.Count];
                        table.Rows.CopyTo (rows, 0);
-                       WriteTable (writer, rows, mode);
+                       WriteTable (writer, rows, mode, version);
                }
 
-               private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode)
+               private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
                {
                        //The columns can be attributes, hidden, elements, or simple content
                        //There can be 0-1 simple content cols or 0-* elements
@@ -994,17 +1075,20 @@ namespace System.Data {
                        if (rows.Length == 0) return;
                        DataTable table = rows[0].Table;
                        SplitColumns (table, out atts, out elements, out simple);
+                       //sort out the namespacing
+                       string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
 
-                       foreach (DataRow row in rows)
-                       {
-                               //sort out the namespacing
-                               string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
-
+                       foreach (DataRow row in rows) {
+                               if (!row.HasVersion(version) || 
+                                  (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
+                                     && version == DataRowVersion.Original))
+                                       continue;
+                               
                                // First check are all the rows null. If they are we just write empty element
                                bool AllNulls = true;
                                foreach (DataColumn dc in table.Columns) {
                                
-                                       if (row [dc.ColumnName] != DBNull.Value) {
+                                       if (row [dc.ColumnName, version] != DBNull.Value) {
                                                AllNulls = false;
                                                break;
                                        } 
@@ -1015,43 +1099,25 @@ namespace System.Data {
                                        writer.WriteElementString (table.TableName, "");
                                        continue;
                                }
-
-                               WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
                                
-                               foreach (DataColumn col in atts)
-                               {                                       
-                                       WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col].ToString ());
-                               }
+                               WriteTableElement (writer, mode, table, row, version);
                                
-                               if (simple != null)
-                               {
-                                       writer.WriteString (WriteObjectXml (row[simple]));
+                               foreach (DataColumn col in atts) {                                      
+                                       WriteColumnAsAttribute (writer, mode, col, row, version);
                                }
-                               else
-                               {                                       
-                                       foreach (DataColumn col in elements)
-                                       {
-                                               string colnspc = nspc;
-                                               object rowObject = row [col];
-                                                                                               
-                                               if (rowObject == null || rowObject == DBNull.Value)
-                                                       continue;
-
-                                               if (col.Namespace != null)
-                                               {
-                                                       colnspc = col.Namespace;
-                                               }
                                
-                                               //TODO check if I can get away with write element string
-                                               WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
-                                               writer.WriteString (WriteObjectXml (rowObject));
-                                               writer.WriteEndElement ();
+                               if (simple != null) {
+                                       writer.WriteString (WriteObjectXml (row[simple, version]));
+                               }
+                               else {                                  
+                                       foreach (DataColumn col in elements) {
+                                               WriteColumnAsElement (writer, mode, nspc, col, row, version);
                                        }
                                }
                                
                                foreach (DataRelation relation in table.ChildRelations) {
                                        if (relation.Nested) {
-                                               WriteTable (writer, row.GetChildRows (relation), mode);
+                                               WriteTable (writer, row.GetChildRows (relation), mode, version);
                                        }
                                }
                                
@@ -1059,46 +1125,95 @@ namespace System.Data {
                        }
 
                }
+
+               private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
+               {
+                       string colnspc = nspc;
+                       object rowObject = row [col, version];
+                                                                       
+                       if (rowObject == null || rowObject == DBNull.Value)
+                               return;
+
+                       if (col.Namespace != null) {
+                               colnspc = col.Namespace;
+                       }
+       
+                       //TODO check if I can get away with write element string
+                       WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
+                       writer.WriteString (WriteObjectXml (rowObject));
+                       writer.WriteEndElement ();
+               }
+
+               private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
+               {
+                       WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
+               }
+
+               private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
+               {
+                       //sort out the namespacing
+                       string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
+
+                       WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
+
+                       if (mode == XmlWriteMode.DiffGram) {
+                               WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
+                               WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
+                               string modeName = null;
+                               if (row.RowState == DataRowState.Modified)
+                                       modeName = "modified";
+                               else if (row.RowState == DataRowState.Added)
+                                       modeName = "inserted";
+
+                               if (version != DataRowVersion.Original && modeName != null)
+                                       WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
+                       }
+               }
                    
                private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
-               {                       
-                       switch ( mode)
-                               {
-                                       case XmlWriteMode.WriteSchema:
-                                               if (nspc == null || nspc == "")
-                                               {
-                                                       writer.WriteStartElement (name);
-                                               }
-                                               else if (prefix != null)
-                                               {                                                       
-                                                       writer.WriteStartElement (prefix, name, nspc);
-                                               }                                               
-                                               else
-                                               {                                       
-                                                       writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
-                                               }
-                                               break;
-                                       case XmlWriteMode.DiffGram:
-                                               throw new NotImplementedException ();
-                                       default:                                               
-                                               writer.WriteStartElement (name);
-                                               break;                                  
-                               };
+               {
+                       writer.WriteStartElement (prefix, name, nspc);
                }
                
                private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
                {
-                       switch ( mode)
-                               {
-                                       case XmlWriteMode.WriteSchema:
-                                               writer.WriteAttributeString (prefix, name, nspc);
-                                               break;
-                                       case XmlWriteMode.DiffGram:
-                                               throw new NotImplementedException ();                           
-                                       default:
-                                               writer.WriteAttributeString (name, stringValue);
-                                               break;                                  
-                               };
+                       switch ( mode) {
+                               case XmlWriteMode.WriteSchema:
+                                       writer.WriteAttributeString (prefix, name, nspc);
+                                       break;
+                               case XmlWriteMode.DiffGram:
+                                       writer.WriteAttributeString (prefix, name, nspc,stringValue);
+                                       break;
+                               default:
+                                       writer.WriteAttributeString (name, stringValue);
+                                       break;                                  
+                       };
+               }
+               
+               internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
+               {
+                       ((XmlTextWriter)writer).Formatting = Formatting.Indented;
+
+                       if (mode == XmlWriteMode.DiffGram) {
+                               SetTableRowsID (table);
+                               WriteDiffGramElement (writer);
+                       }
+                       
+                       WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
+                       
+                       WriteTable (writer, table, mode, DataRowVersion.Default);
+                       
+                       if (mode == XmlWriteMode.DiffGram) {
+                               writer.WriteEndElement (); //DataSet name
+                               if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
+
+                                       DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
+                                       WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
+                                       WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
+                                       writer.WriteEndElement ();
+                               }
+                       }
+                       writer.WriteEndElement (); // DataSet name or diffgr:diffgram
                }
 
                XmlSchema IXmlSerializable.GetSchema ()
@@ -1108,20 +1223,43 @@ namespace System.Data {
                
                XmlSchema BuildSchema ()
                {
+                       return BuildSchema (Tables, Relations);
+               }
+               
+               internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
+               {
+                       string constraintPrefix = "";
                        XmlSchema schema = new XmlSchema ();
-                       schema.AttributeFormDefault = XmlSchemaForm.Qualified;
+
+                       schema.Namespaces.Add("xs", XmlSchema.Namespace);
+                       schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
+                       
+                       if (Namespace != "" && Namespace != null) {
+                               schema.AttributeFormDefault = XmlSchemaForm.Qualified;
+                               schema.ElementFormDefault = XmlSchemaForm.Qualified;
+                               schema.TargetNamespace = Namespace;
+                               schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
+                               constraintPrefix = XmlConstants.TnsPrefix + ":";
+                       }
+                               
+                       // set the schema id
+                       schema.Id = DataSetName;
+                       XmlDocument doc = new XmlDocument ();
+                       XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
+                       xmlnsAttr.Value = Namespace;
+
+                       schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
 
                        XmlSchemaElement elem = new XmlSchemaElement ();
                        elem.Name = XmlConvert.EncodeName (DataSetName);
 
-                       XmlDocument doc = new XmlDocument ();
-
                        XmlAttribute[] atts = new XmlAttribute [2];
                        atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
                        atts[0].Value = "true";
 
                        atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
                        atts[1].Value = locale.Name;
+
                        elem.UnhandledAttributes = atts;
 
                        schema.Items.Add (elem);
@@ -1134,7 +1272,7 @@ namespace System.Data {
                        choice.MaxOccursString = XmlConstants.Unbounded;
                        
                        //Write out schema for each table in order
-                       foreach (DataTable table in Tables) {           
+                       foreach (DataTable table in tables) {           
                                bool isTopLevel = true;
                                foreach (DataRelation rel in table.ParentRelations) {
                                        if (rel.Nested) {
@@ -1148,87 +1286,127 @@ namespace System.Data {
                                }
                        }
                        
-                       bool nameModifier = true;
-                       foreach (DataRelation rel in Relations) {
-                               XmlSchemaUnique uniq = new XmlSchemaUnique();
-                               XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
-                               ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
-                               UniqueConstraint uqConst = rel.ParentKeyConstraint;
-                                                               
-                               if (nameModifier) {
-                                       uniq.Name = uqConst.ConstraintName;
-                                       keyRef.Name = fkConst.ConstraintName;
-                                       keyRef.Refer = new XmlQualifiedName(uniq.Name);
-                                       XmlAttribute[] attrib = null;
-                                       if (rel.Nested){
-                                               attrib = new XmlAttribute [2];
-                                               attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
-                                               attrib [0].Value = "true";
+                       AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
+                       return schema;
+               }
                
-                                               attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
-                                               attrib [1].Value = rel.RelationName;
-                                       }
-                                       else {
-                                               attrib = new XmlAttribute [1];
-                                               attrib[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
-                                               attrib[0].Value = rel.RelationName;
+               // Add all constraints in all tables to the schema.
+               private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations) 
+               {
+                       // first add all unique constraints.
+                       Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
+                       // Add all foriegn key constraints.
+                       AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
+               }
+               
+               // Add unique constaraints to the schema.
+               // return hashtable with the names of all XmlSchemaUnique elements we created.
+               private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
+               {
+                       XmlDocument doc = new XmlDocument();
+                       Hashtable uniqueNames = new Hashtable();
+                       foreach (DataTable table in tables) {
+                               
+                               foreach (Constraint constaint in table.Constraints) {
+                                       
+                                       if (constaint is UniqueConstraint) {
+                                               ArrayList attrs = new ArrayList ();
+                                               XmlAttribute attrib;
+                                               UniqueConstraint uqConst = (UniqueConstraint)constaint;
+                                               XmlSchemaUnique uniq = new XmlSchemaUnique ();
+                                               
+                                               // if column of the constraint is hidden do not write the constraint.
+                                               bool isHidden = false;
+                                               foreach (DataColumn column in uqConst.Columns) {
+                                                       if (column.ColumnMapping == MappingType.Hidden) {
+                                                               isHidden = true;
+                                                               break;
+                                                       }
+                                               }
+
+                                               if (isHidden)
+                                                       continue;
 
+                                               // if constaraint name do not exist in the hashtable we can use it.
+                                               if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
+                                                       uniq.Name = uqConst.ConstraintName;
+                                               }
+                                               // generate new constraint name for the XmlSchemaUnique element.
+                                               else {
+                                                       uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
+                                                       attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
+                                                       attrib.Value = uqConst.ConstraintName;
+                                                       attrs.Add (attrib);
+                                               }
+                                               if (uqConst.IsPrimaryKey) {
+                                                       attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
+                                                       attrib.Value = "true";
+                                                       attrs.Add (attrib);
+                                               }
+               
+                                               uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
+
+                                               uniq.Selector = new XmlSchemaXPath();
+                                               uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
+                                               XmlSchemaXPath field;
+                                               foreach (DataColumn column in uqConst.Columns) {
+                                                       field = new XmlSchemaXPath();
+                                                       field.XPath = constraintPrefix+column.ColumnName;
+                                                       uniq.Fields.Add(field);
+                                               }
+                               
+                                               elem.Constraints.Add (uniq);
+                                               uniqueNames.Add (uniq.Name, null);
                                        }
-                                       keyRef.UnhandledAttributes = attrib;
-                                       nameModifier = false;
                                }
-                               else {
-                                       uniq.Name = rel.ParentTable.TableName+"_"+uqConst.ConstraintName;
-                                       keyRef.Name = rel.ChildTable.TableName+"_"+fkConst.ConstraintName;
-                                       keyRef.Refer = new XmlQualifiedName(uniq.Name);
-                                       XmlAttribute[] attrib;
-                                       if (rel.Nested) {
-                                               attrib = new XmlAttribute [3];
-                                               attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
-                                               attrib [0].Value = fkConst.ConstraintName;
-                                               attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
-                                               attrib [1].Value = "true";
+                       }
+                       return uniqueNames;
+               }
                
-                                               attrib [2] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
-                                               attrib [2].Value = rel.RelationName;
-                                       }
-                                       else {
-                                               attrib = new XmlAttribute [2];
-                                               attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
-                                               attrib [0].Value = fkConst.ConstraintName;
-                                               attrib [1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.RelationName, XmlConstants.MsdataNamespace);
-                                               attrib [1].Value = rel.RelationName;
-
-                                       }
-                                       keyRef.UnhandledAttributes = attrib;
-                                       attrib = new XmlAttribute [1];
-                                       attrib [0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
-                                       attrib [0].Value = uqConst.ConstraintName; 
-                                       uniq.UnhandledAttributes = attrib;
+               // Add the foriegn keys to the schema.
+               private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
+               {
+                       if (relations == null) return;
+                       
+                       XmlDocument doc = new XmlDocument();
+                       foreach (DataRelation rel in relations) {
+                               
+                               if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
+                                       continue;
+                               
+                               ArrayList attrs = new ArrayList ();
+                               XmlAttribute attrib;
+                               XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
+                               keyRef.Name = rel.RelationName;
+                               ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
+                               UniqueConstraint uqConst = rel.ParentKeyConstraint;
+                               
+                               string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
+                               // first try to find the concatenated name. If we didn't find it - use constraint name.
+                               if (uniqueNames.ContainsKey (concatName)) {
+                                       keyRef.Refer = new XmlQualifiedName(concatName);
+                               }
+                               else {
+                                       keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
                                }
 
-                               uniq.Selector = new XmlSchemaXPath();
-                               uniq.Selector.XPath = ".//"+rel.ParentTable.TableName;
-                               XmlSchemaXPath field;
-                               foreach (DataColumn column in rel.ParentColumns) {
-                                       field = new XmlSchemaXPath();
-                                       field.XPath = column.ColumnName;
-                                       uniq.Fields.Add(field);
+                               if (rel.Nested) {
+                                       attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
+                                       attrib.Value = "true";
+                                       attrs.Add (attrib);
                                }
-                               
+
                                keyRef.Selector = new XmlSchemaXPath();
-                               keyRef.Selector.XPath = ".//"+rel.ChildTable.TableName;
+                               keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
+                               XmlSchemaXPath field;
                                foreach (DataColumn column in rel.ChildColumns) {
                                        field = new XmlSchemaXPath();
-                                       field.XPath = column.ColumnName;
+                                       field.XPath = constraintPrefix+column.ColumnName;
                                        keyRef.Fields.Add(field);
                                }
-                               
-                               elem.Constraints.Add (uniq);
+                               keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
                                elem.Constraints.Add (keyRef);
                        }
-                       
-                       return schema;
                }
 
                private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
@@ -1246,32 +1424,72 @@ namespace System.Data {
                        elem.SchemaType = complex;
 
                        //TODO - what about the simple content?
-                       if (elements.Count == 0) 
-                       {                               
+                       if (simple != null) {
+                               // add simpleContent
+                               XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
+                               complex.ContentModel = simpleContent;
+
+                               // add column name attribute
+                               XmlAttribute[] xlmAttrs = new XmlAttribute [2];
+                               xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
+                               xlmAttrs[0].Value = simple.ColumnName;
+                               
+                               // add ordinal attribute
+                               xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
+                               xlmAttrs[1].Value = simple.Ordinal.ToString();
+                               simpleContent.UnhandledAttributes = xlmAttrs;
+                               
+                               
+                               // add extension
+                               XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
+                               simpleContent.Content = extension;
+                               extension.BaseTypeName = MapType (simple.DataType);
+                       
                        }
-                       else 
-                       {
+                       else {
                                //A sequence of element types or a simple content node
                                //<xs:sequence>
                                XmlSchemaSequence seq = new XmlSchemaSequence ();
                                complex.Particle = seq;
 
                                foreach (DataColumn col in elements) {
-                                       //<xs:element name=ColumnName type=MappedType Ordinal=index>
+                                       
+                                       // Add element for the column.
                                        XmlSchemaElement colElem = new XmlSchemaElement ();
+                                       ArrayList xattrs = new ArrayList();
+                                       XmlAttribute xattr;
                                        colElem.Name = col.ColumnName;
                                
-                                       if (col.ColumnName != col.Caption && col.Caption != string.Empty) {
-                                               XmlAttribute[] xatts = new XmlAttribute[1];
-                                               xatts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
-                                               xatts[0].Value = col.Caption;
-                                               colElem.UnhandledAttributes = xatts;
+                                       if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
+                                               xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
+                                               xattr.Value = col.Caption;
+                                               xattrs.Add (xattr);
                                        }
 
-                                       if (col.DefaultValue.ToString () != string.Empty)
-                                               colElem.DefaultValue = col.DefaultValue.ToString ();
+                                       if (col.AutoIncrement == true) {
+                                               xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
+                                               xattr.Value = "true";
+                                               xattrs.Add (xattr);
+                                       }
 
-                                       colElem.SchemaTypeName = MapType (col.DataType);
+                                       if (col.AutoIncrementSeed != 0) {
+                                               xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
+                                               xattr.Value = col.AutoIncrementSeed.ToString();
+                                               xattrs.Add (xattr);
+                                       }
+
+                                       if (col.DefaultValue.ToString () != String.Empty)
+                                               colElem.DefaultValue = col.DefaultValue.ToString ();
+                                       
+                                       if (col.MaxLength < 0)
+                                               colElem.SchemaTypeName = MapType (col.DataType);
+                                       
+                                       if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
+                                               && col.DataType != typeof (char)) {
+                                               xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
+                                               xattr.Value = col.DataType.ToString();
+                                               xattrs.Add (xattr);
+                                       }
 
                                        if (col.AllowDBNull) {
                                                colElem.MinOccurs = 0;
@@ -1286,7 +1504,8 @@ namespace System.Data {
                                        if (col.MaxLength > -1) {
                                                colElem.SchemaType = GetTableSimpleType (doc, col);
                                        }
-
+                                       
+                                       colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
                                        seq.Items.Add (colElem);
                                }
 
@@ -1306,6 +1525,7 @@ namespace System.Data {
                                att.SchemaTypeName = MapType (col.DataType);
                                complex.Attributes.Add (att);
                        }
+
                        return elem;
                }
 
@@ -1322,13 +1542,14 @@ namespace System.Data {
                        XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
                        max.Value = XmlConvert.ToString (col.MaxLength);
                        restriction.Facets.Add (max);
-
+                       
+                       simple.Content = restriction;
                        return simple;
                }
 
                private void DoWriteXmlSchema (XmlWriter writer)
                {
-                       GetSchemaSerializable ().Write (writer);
+                       BuildSchema ().Write (writer);
                }
                
                ///<summary>
@@ -1336,9 +1557,9 @@ namespace System.Data {
                /// content
                /// </summary>
                private void SplitColumns (DataTable table, 
-                                          out ArrayList atts, 
-                                          out ArrayList elements, 
-                                          out DataColumn simple)
+                       out ArrayList atts, 
+                       out ArrayList elements, 
+                       out DataColumn simple)
                {
                        //The columns can be attributes, hidden, elements, or simple content
                        //There can be 0-1 simple content cols or 0-* elements
@@ -1348,8 +1569,7 @@ namespace System.Data {
                        
                        //Sort out the columns
                        foreach (DataColumn col in table.Columns) {
-                               switch (col.ColumnMapping)
-                               {
+                               switch (col.ColumnMapping) {
                                        case MappingType.Attribute:
                                                atts.Add (col);
                                                break;
@@ -1357,8 +1577,7 @@ namespace System.Data {
                                                elements.Add (col);
                                                break;
                                        case MappingType.SimpleContent:
-                                               if (simple != null)
-                                               {
+                                               if (simple != null) {
                                                        throw new System.InvalidOperationException ("There may only be one simple content element");
                                                }
                                                simple = col;
@@ -1369,18 +1588,39 @@ namespace System.Data {
                                }
                        }
                }
+
+               private void WriteDiffGramElement(XmlWriter writer)
+               {
+                       WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
+                       WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
+               }
+
+               private void SetRowsID()
+               {
+                       foreach (DataTable Table in Tables)
+                               SetTableRowsID (Table);
+               }
+               
+               private void SetTableRowsID (DataTable Table)
+               {
+                       int dataRowID = 0;
+                       foreach (DataRow Row in Table.Rows) {
+                               Row.XmlRowID = dataRowID;
+                               dataRowID++;
+                       }
+               }
+
                
                private XmlQualifiedName MapType (Type type)
                {
-                       switch (Type.GetTypeCode (type))
-                       {
+                       switch (Type.GetTypeCode (type)) {
                                case TypeCode.String: return XmlConstants.QnString;
                                case TypeCode.Int16: return XmlConstants.QnShort;
                                case TypeCode.Int32: return XmlConstants.QnInt;
                                case TypeCode.Int64: return XmlConstants.QnLong;
                                case TypeCode.Boolean: return XmlConstants.QnBoolean;
                                case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
-                               case TypeCode.Char: return XmlConstants.QnChar;
+                               //case TypeCode.Char: return XmlConstants.QnChar;
                                case TypeCode.DateTime: return XmlConstants.QnDateTime;
                                case TypeCode.Decimal: return XmlConstants.QnDecimal;
                                case TypeCode.Double: return XmlConstants.QnDouble;