X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Data%2FSystem.Data%2FDataSet.cs;h=741c6a2b74738a50f467a028ca6b225e1cc1fc52;hb=12d6be3dc6e836bf81967711388ad7c019d2053a;hp=d4362a9652dc7e6efadbd9fbda78d0e95a98b804;hpb=f11ff988040c8160eaad728230af29e1f8f52a07;p=mono.git diff --git a/mcs/class/System.Data/System.Data/DataSet.cs b/mcs/class/System.Data/System.Data/DataSet.cs index d4362a9652d..741c6a2b747 100644 --- a/mcs/class/System.Data/System.Data/DataSet.cs +++ b/mcs/class/System.Data/System.Data/DataSet.cs @@ -8,9 +8,10 @@ // Stuart Caborn // Tim Coleman (tim@timcoleman.com) // Ville Palo +// Atsushi Enomoto // // (C) Ximian, Inc. 2002 -// Copyright (C) Tim Coleman, 2002 +// Copyright (C) Tim Coleman, 2002, 2003 // using System; @@ -26,15 +27,13 @@ using System.Xml.Serialization; using System.Data.Common; namespace System.Data { - /// - /// an in-memory cache of data - /// - //[Designer] + [ToolboxItem (false)] [DefaultProperty ("DataSetName")] [Serializable] - public class DataSet : MarshalByValueComponent, IListSource, - ISupportInitialize, ISerializable, IXmlSerializable { + public class DataSet : MarshalByValueComponent, IListSource, + ISupportInitialize, ISerializable, IXmlSerializable + { private string dataSetName; private string _namespace = ""; private string prefix; @@ -45,21 +44,28 @@ namespace System.Data { private PropertyCollection properties; private DataViewManager defaultView; private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture; + internal XmlDataDocument _xmlDataDocument = null; #region Constructors - public DataSet() : this ("NewDataSet") { + public DataSet () : this ("NewDataSet") + { } - - public DataSet(string name) { + + public DataSet (string name) + { dataSetName = name; tableCollection = new DataTableCollection (this); relationCollection = new DataRelationCollection.DataSetRelationCollection (this); - properties = new PropertyCollection(); + properties = new PropertyCollection (); + this.prefix = String.Empty; + + this.Locale = CultureInfo.CurrentCulture; } [MonoTODO] - protected DataSet(SerializationInfo info, StreamingContext context) : this () { + protected DataSet (SerializationInfo info, StreamingContext context) : this () + { throw new NotImplementedException (); } @@ -71,14 +77,17 @@ namespace System.Data { [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")] [DefaultValue (false)] public bool CaseSensitive { - get { return caseSensitive; } + get { + return caseSensitive; + } set { - foreach (DataTable T in Tables) { - if (T.VirginCaseSensitive) - T.CaseSensitive = value; - } - caseSensitive = value; + if (!caseSensitive) { + foreach (DataTable table in Tables) { + foreach (Constraint c in table.Constraints) + c.AssertConstraint (); + } + } } } @@ -104,7 +113,21 @@ namespace System.Data { [DefaultValue (true)] public bool EnforceConstraints { get { return enforceConstraints; } - set { enforceConstraints = value; } + set { + if (value != enforceConstraints) { + enforceConstraints = value; + if (value) { + foreach (DataTable table in Tables) { + // 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 (); + } + } + } + } } [Browsable (false)] @@ -119,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; } @@ -135,7 +157,7 @@ namespace System.Data { return locale; } set { - if (locale == null || !locale.Equals(value)) { + if (locale == null || !locale.Equals (value)) { // TODO: check if the new locale is valid // TODO: update locale of all tables locale = value; @@ -166,37 +188,37 @@ namespace System.Data { [MonoTODO] public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction) { - if(rows == null) - throw new ArgumentNullException("rows"); - if(!IsLegalSchemaAction(missingSchemaAction)) - throw new ArgumentOutOfRangeException("missingSchemaAction"); + if (rows == null) + throw new ArgumentNullException ("rows"); + if (!IsLegalSchemaAction (missingSchemaAction)) + throw new ArgumentOutOfRangeException ("missingSchemaAction"); - MergeManager.Merge(this, rows, preserveChanges, missingSchemaAction); + MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction); } [MonoTODO] public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction) { - if(dataSet == null) - throw new ArgumentNullException("dataSet"); - if(!IsLegalSchemaAction(missingSchemaAction)) - throw new ArgumentOutOfRangeException("missingSchemaAction"); + if (dataSet == null) + throw new ArgumentNullException ("dataSet"); + if (!IsLegalSchemaAction (missingSchemaAction)) + throw new ArgumentOutOfRangeException ("missingSchemaAction"); - MergeManager.Merge(this, dataSet, preserveChanges, missingSchemaAction); + MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction); } [MonoTODO] public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction) { - if(table == null) - throw new ArgumentNullException("table"); - if(!IsLegalSchemaAction(missingSchemaAction)) - throw new ArgumentOutOfRangeException("missingSchemaAction"); + if (table == null) + throw new ArgumentNullException ("table"); + if (!IsLegalSchemaAction (missingSchemaAction)) + throw new ArgumentOutOfRangeException ("missingSchemaAction"); - MergeManager.Merge(this, table, preserveChanges, missingSchemaAction); + MergeManager.Merge (this, table, preserveChanges, missingSchemaAction); } - private static bool IsLegalSchemaAction(MissingSchemaAction missingSchemaAction) + private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction) { if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore) @@ -208,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; } } @@ -221,11 +243,20 @@ 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; + + if (value != this.prefix) + RaisePropertyChanging ("Prefix"); prefix = value; } } @@ -265,22 +296,22 @@ namespace System.Data { #region Public Methods [MonoTODO] - public void AcceptChanges() + public void AcceptChanges () { foreach (DataTable tempTable in tableCollection) tempTable.AcceptChanges (); } - public void Clear() + 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 (); } } - public virtual DataSet Clone() + public virtual DataSet Clone () { DataSet Copy = new DataSet (); CopyProperties (Copy); @@ -291,30 +322,28 @@ namespace System.Data { //Copy Relationships between tables after existance of tables //and setting properties correctly - CopyRelations(Copy); + CopyRelations (Copy); return Copy; } // Copies both the structure and data for this DataSet. - public DataSet Copy() + public DataSet Copy () { DataSet Copy = new DataSet (); CopyProperties (Copy); // Copy DatSet's tables - foreach (DataTable Table in Tables) { + foreach (DataTable Table in Tables) Copy.Tables.Add (Table.Copy ()); - } //Copy Relationships between tables after existance of tables //and setting properties correctly - CopyRelations(Copy); + CopyRelations (Copy); return Copy; } - [MonoTODO] private void CopyProperties (DataSet Copy) { Copy.CaseSensitive = CaseSensitive; @@ -323,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. } @@ -336,91 +370,122 @@ namespace System.Data { private void CopyRelations (DataSet Copy) { - //Creation of the relation contains some of the properties, and the constructor - //demands these values. instead changing the DataRelation constructor and behaviour the + //Creation of the relation contains some of the properties, and the constructor + //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) - { - string pTable = MyRelation.ParentTable.TableName; - string cTable = MyRelation.ChildTable.TableName; - DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; - DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length]; - int i = 0; - foreach(DataColumn DC in MyRelation.ParentColumns) - { - P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName]; - i++; - } - - i = 0; - - foreach(DataColumn DC in MyRelation.ChildColumns) - { - C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName]; - i++; - } - - - DataRelation cRel = new DataRelation(MyRelation.RelationName,P_DC,C_DC); - //cRel.ChildColumns = MyRelation.ChildColumns; - //cRel.ChildTable = MyRelation.ChildTable; - //cRel.ExtendedProperties = cRel.ExtendedProperties; - //cRel.Nested = MyRelation.Nested; - //cRel.ParentColumns = MyRelation.ParentColumns; - //cRel.ParentTable = MyRelation.ParentTable; - - Copy.Relations.Add(cRel); + foreach (DataRelation MyRelation in this.Relations) { + string pTable = MyRelation.ParentTable.TableName; + string cTable = MyRelation.ChildTable.TableName; + DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; + DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length]; + int i = 0; + + foreach (DataColumn DC in MyRelation.ParentColumns) { + P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName]; + i++; + } + + i = 0; + + foreach (DataColumn DC in MyRelation.ChildColumns) { + C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName]; + i++; + } + + DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC); + //cRel.ChildColumns = MyRelation.ChildColumns; + //cRel.ChildTable = MyRelation.ChildTable; + //cRel.ExtendedProperties = cRel.ExtendedProperties; + //cRel.Nested = MyRelation.Nested; + //cRel.ParentColumns = MyRelation.ParentColumns; + //cRel.ParentTable = MyRelation.ParentTable; + + Copy.Relations.Add (cRel); } } - public DataSet GetChanges() + public DataSet GetChanges () { - return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); + return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); } - public DataSet GetChanges(DataRowState rowStates) + public DataSet GetChanges (DataRowState rowStates) { - if(!HasChanges(rowStates)) + if (!HasChanges (rowStates)) return null; - DataSet copySet = Clone(); - IEnumerator tableEnumerator = Tables.GetEnumerator(); + DataSet copySet = Clone (); + Hashtable addedRows = new Hashtable (); + + IEnumerator tableEnumerator = Tables.GetEnumerator (); DataTable origTable; DataTable copyTable; - while (tableEnumerator.MoveNext()) - { + 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()) - { + 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); - } + + if (row.IsRowChanged (rowStates)) + 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] + public DataTableReader GetDataReader (DataTable[] dataTables) + { + throw new NotImplementedException (); + } + + [MonoTODO] + public DataTableReader GetDataReader () + { + throw new NotImplementedException (); + } +#endif - public string GetXml() + public string GetXml () { StringWriter Writer = new StringWriter (); + + // Sending false for not printing the Processing instruction WriteXml (Writer, XmlWriteMode.IgnoreSchema); return Writer.ToString (); } - public string GetXmlSchema() + public string GetXmlSchema () { StringWriter Writer = new StringWriter (); WriteXmlSchema (Writer); @@ -428,29 +493,28 @@ namespace System.Data { } [MonoTODO] - public bool HasChanges() + public bool HasChanges () { - return HasChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); + return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); } [MonoTODO] - public bool HasChanges(DataRowState rowState) + public bool HasChanges (DataRowState rowState) { - if(((int)rowState & 0xffffffe0) != 0) - throw new ArgumentOutOfRangeException("rowState"); + if (((int)rowState & 0xffffffe0) != 0) + throw new ArgumentOutOfRangeException ("rowState"); DataTableCollection tableCollection = Tables; DataTable table; DataRowCollection rowCollection; DataRow row; - for (int i = 0; i < tableCollection.Count; i++) - { + + for (int i = 0; i < tableCollection.Count; i++) { table = tableCollection[i]; rowCollection = table.Rows; - for (int j = 0; j < rowCollection.Count; j++) - { + for (int j = 0; j < rowCollection.Count; j++) { row = rowCollection[j]; - if((row.RowState & rowState) != 0) + if ((row.RowState & rowState) != 0) return true; } } @@ -458,24 +522,26 @@ namespace System.Data { return false; } - [MonoTODO] - public void InferXmlSchema(XmlReader reader, string[] nsArray) + [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) + public void InferXmlSchema (Stream stream, string[] nsArray) { - InferXmlSchema (new XmlTextReader(stream), nsArray); + InferXmlSchema (new XmlTextReader (stream), nsArray); } - public void InferXmlSchema(TextReader reader, string[] nsArray) + public void InferXmlSchema (TextReader reader, string[] nsArray) { - InferXmlSchema (new XmlTextReader(reader), nsArray); + InferXmlSchema (new XmlTextReader (reader), nsArray); } - public void InferXmlSchema(string fileName, string[] nsArray) + public void InferXmlSchema (string fileName, string[] nsArray) { - XmlTextReader reader = new XmlTextReader(fileName); + XmlTextReader reader = new XmlTextReader (fileName); try { InferXmlSchema (reader, nsArray); } finally { @@ -483,177 +549,212 @@ namespace System.Data { } } - public virtual void RejectChanges() +#if NET_1_2 + [MonoTODO] + public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables) { - throw new NotImplementedException(); + throw new NotImplementedException (); } - public virtual void Reset() + [MonoTODO] + public void Load (IDataReader reader, LoadOption loadOption, string[] tables) + { + throw new NotImplementedException (); + } +#endif + + public virtual void RejectChanges () + { + int i; + bool oldEnforceConstraints = this.EnforceConstraints; + this.EnforceConstraints = false; + + for (i = 0; i < this.Tables.Count;i++) + this.Tables[i].RejectChanges (); + + this.EnforceConstraints = oldEnforceConstraints; + } + + public virtual void Reset () { IEnumerator constraintEnumerator; + // first we remove all ForeignKeyConstraints (if we will not do that // we will get an exception when clearing the tables). - for (int i = 0; i < Tables.Count; i++) - { + for (int i = 0; i < Tables.Count; i++) { ConstraintCollection cc = Tables[i].Constraints; - for (int j = 0; j < cc.Count; j++) - { + for (int j = 0; j < cc.Count; j++) { if (cc[j] is ForeignKeyConstraint) - cc.Remove(cc[j]); + cc.Remove (cc[j]); } } - Clear(); - Relations.Clear(); - Tables.Clear(); + Clear (); + Relations.Clear (); + Tables.Clear (); } - public void WriteXml(Stream stream) + public void WriteXml (Stream stream) { - XmlWriter writer = new XmlTextWriter(stream, null ); - - WriteXml( writer ); + XmlTextWriter writer = new XmlTextWriter (stream, null); + writer.Formatting = Formatting.Indented; + WriteXml (writer); } /// /// Writes the current data for the DataSet to the specified file. /// /// Fully qualified filename to write to - public void WriteXml(string fileName) + 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) + public void WriteXml (TextWriter writer) { - XmlWriter xwriter = new XmlTextWriter(writer ); - - WriteXml( xwriter ); + XmlTextWriter xwriter = new XmlTextWriter (writer); + xwriter.Formatting = Formatting.Indented; + WriteXml (xwriter); } - public void WriteXml(XmlWriter writer) + public void WriteXml (XmlWriter writer) { - WriteXml( writer, XmlWriteMode.IgnoreSchema ); + WriteXml (writer, XmlWriteMode.IgnoreSchema); } - public void WriteXml(Stream stream, XmlWriteMode mode) + public void WriteXml (string filename, XmlWriteMode mode) { - XmlWriter writer = new XmlTextWriter(stream, null ); + XmlTextWriter writer = new XmlTextWriter (filename, null); + writer.Formatting = Formatting.Indented; + writer.WriteStartDocument (true); - WriteXml( writer, mode ); + try { + WriteXml (writer, mode); + } + finally { + writer.WriteEndDocument (); + writer.Close (); + } } - public void WriteXml(string fileName, XmlWriteMode mode) + public void WriteXml (Stream stream, XmlWriteMode mode) { - XmlWriter writer = new XmlTextWriter(fileName, null ); - - WriteXml( writer, mode ); - - writer.Close(); + XmlTextWriter writer = new XmlTextWriter (stream, null); + writer.Formatting = Formatting.Indented; + WriteXml (writer, mode); } - public void WriteXml(TextWriter writer, XmlWriteMode mode) + public void WriteXml (TextWriter writer, XmlWriteMode mode) { - XmlWriter xwriter = new XmlTextWriter(writer); - - WriteXml( xwriter, mode ); + XmlTextWriter xwriter = new XmlTextWriter (writer); + xwriter.Formatting = Formatting.Indented; + WriteXml (xwriter, mode); } - public void WriteXml(XmlWriter writer, XmlWriteMode mode) + public void WriteXml (XmlWriter writer, XmlWriteMode mode) { - if (writer.WriteState == WriteState.Start) - writer.WriteStartDocument (true); + if (mode == XmlWriteMode.DiffGram) { + SetRowsID(); + WriteDiffGramElement(writer); + } - ((XmlTextWriter)writer).Formatting = Formatting.Indented; - WriteStartElement( writer, mode, Namespace, Prefix, DataSetName ); + string ns = Namespace == null ? String.Empty : Namespace; + + WriteStartElement (writer, mode, ns, Prefix, XmlConvert.EncodeName (DataSetName)); - if( mode == XmlWriteMode.WriteSchema ) - { - DoWriteXmlSchema( writer ); + 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) + public void WriteXmlSchema (Stream stream) { - XmlWriter writer = new XmlTextWriter(stream, null ); - - WriteXmlSchema( writer ); + XmlTextWriter writer = new XmlTextWriter (stream, null ); + writer.Formatting = Formatting.Indented; + WriteXmlSchema (writer); } - public void WriteXmlSchema(string fileName) + 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) + public void WriteXmlSchema (TextWriter writer) { - XmlWriter xwriter = new XmlTextWriter( writer ); - - WriteXmlSchema( xwriter ); + XmlTextWriter xwriter = new XmlTextWriter (writer); + xwriter.Formatting = Formatting.Indented; + WriteXmlSchema (xwriter); } - public void WriteXmlSchema(XmlWriter writer) + 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(); + DoWriteXmlSchema (writer); } - public void ReadXmlSchema(Stream stream) + public void ReadXmlSchema (Stream stream) { - XmlReader reader = new XmlTextReader( stream, null ); - ReadXmlSchema( reader); + XmlReader reader = new XmlTextReader (stream, null); + ReadXmlSchema (reader); } - public void ReadXmlSchema(string str) + public void ReadXmlSchema (string str) { - XmlReader reader = new XmlTextReader( str ); - ReadXmlSchema( reader ); + XmlReader reader = new XmlTextReader (str); + try { + ReadXmlSchema (reader); + } + finally { + reader.Close (); + } } - public void ReadXmlSchema(TextReader treader) + public void ReadXmlSchema (TextReader treader) { - XmlReader reader = new XmlTextReader( treader ); - ReadXmlSchema( reader ); + XmlReader reader = new XmlTextReader (treader); + ReadXmlSchema (reader); } - public void ReadXmlSchema(XmlReader reader) + 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) @@ -663,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) @@ -673,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) @@ -709,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) @@ -720,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 @@ -747,7 +891,7 @@ namespace System.Data { #region Destructors - ~DataSet() + ~DataSet () { } @@ -769,12 +913,10 @@ namespace System.Data { #region ISupportInitialize methods public void BeginInit () { - throw new NotImplementedException (); } public void EndInit () { - throw new NotImplementedException (); } #endregion @@ -786,42 +928,37 @@ namespace System.Data { #endregion #region Protected Methods - protected void GetSerializationData(SerializationInfo info, StreamingContext context) + protected void GetSerializationData (SerializationInfo info, StreamingContext context) { string s = info.GetValue ("XmlDiffGram", typeof (String)) as String; - if (s != null) ReadXmlSerializable (new XmlTextReader(new StringReader(s))); + if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s))); } - protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable() + protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable () { - return BuildSchema (); + return null; } - protected virtual void ReadXmlSerializable(XmlReader reader) + protected virtual void ReadXmlSerializable (XmlReader reader) { - ReadXml(reader, XmlReadMode.DiffGram); // FIXME + ReadXml (reader, XmlReadMode.DiffGram); // FIXME } - void IXmlSerializable.ReadXml(XmlReader reader) + void IXmlSerializable.ReadXml (XmlReader reader) { - reader.MoveToContent (); - reader.ReadStartElement (); // - - reader.MoveToContent (); - ReadXmlSchema (reader); - - reader.MoveToContent (); - ReadXml(reader, XmlReadMode.IgnoreSchema); - reader.MoveToContent (); - reader.ReadEndElement (); // + ReadXmlSerializable(reader); + + // the XmlSerializationReader does this lines!!! + //reader.MoveToContent (); + //reader.ReadEndElement (); // } - void IXmlSerializable.WriteXml(XmlWriter writer) + void IXmlSerializable.WriteXml (XmlWriter writer) { DoWriteXmlSchema (writer); - WriteXml(writer, XmlWriteMode.IgnoreSchema); + WriteXml (writer, XmlWriteMode.DiffGram); } protected virtual bool ShouldSerializeRelations () @@ -852,7 +989,7 @@ namespace System.Data { protected internal virtual void OnMergeFailed (MergeFailedEventArgs e) { if (MergeFailed != null) - MergeFailed(this, e); + MergeFailed (this, e); } [MonoTODO] @@ -863,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); - return o.ToString(); + 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 @@ -916,18 +1074,21 @@ namespace System.Data { if (rows.Length == 0) return; DataTable table = rows[0].Table; - SplitColumns( table, out atts, out elements, out simple ); - - foreach( DataRow row in rows ) - { - //sort out the namespacing - string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace; + 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) { + 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; } @@ -938,106 +1099,159 @@ 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); } } - writer.WriteEndElement(); + writer.WriteEndElement (); } } + + 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; - }; + private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name) + { + 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: + writer.WriteAttributeString (prefix, name, nspc,stringValue); + break; + default: + writer.WriteAttributeString (name, stringValue); + break; + }; } - private void WriteAttributeString( XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue ) + internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode) { - switch( mode ) - { - case XmlWriteMode.WriteSchema: - writer.WriteAttributeString(prefix, name, nspc ); - break; - case XmlWriteMode.DiffGram: - throw new NotImplementedException(); - default: - writer.WriteAttributeString(name, stringValue ); - break; - }; + ((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() + XmlSchema IXmlSerializable.GetSchema () { return BuildSchema (); } - XmlSchema BuildSchema() + XmlSchema BuildSchema () { + return BuildSchema (Tables, Relations); + } + + internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations) + { + string constraintPrefix = ""; XmlSchema schema = new XmlSchema (); - schema.AttributeFormDefault = XmlSchemaForm.Qualified; - - XmlSchemaElement elem = new XmlSchemaElement (); - elem.Name = DataSetName; + 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); XmlAttribute[] atts = new XmlAttribute [2]; atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.IsDataSet, XmlConstants.MsdataNamespace); @@ -1045,6 +1259,7 @@ namespace System.Data { atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace); atts[1].Value = locale.Name; + elem.UnhandledAttributes = atts; schema.Items.Add (elem); @@ -1056,30 +1271,143 @@ namespace System.Data { complex.Particle = choice; choice.MaxOccursString = XmlConstants.Unbounded; - //Write out schema for each table in order, providing it is not - //part of another table structure via a nested parent relationship - foreach( DataTable table in Tables ) - { + //Write out schema for each table in order + foreach (DataTable table in tables) { bool isTopLevel = true; - foreach( DataRelation rel in table.ParentRelations ) - { - if( rel.Nested ) - { + foreach (DataRelation rel in table.ParentRelations) { + if (rel.Nested) { isTopLevel = false; break; } } - if( isTopLevel ) - { + if (isTopLevel){ choice.Items.Add (GetTableSchema (doc, table)); } } - //TODO - now add in the relationships as key and unique constraints etc - + AddConstraintsToSchema (elem, constraintPrefix, tables, relations); return schema; } + + // 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); + } + } + } + return uniqueNames; + } + + // 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); + } + + 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 = ".//" + constraintPrefix + rel.ChildTable.TableName; + XmlSchemaXPath field; + foreach (DataColumn column in rel.ChildColumns) { + field = new XmlSchemaXPath(); + field.XPath = constraintPrefix+column.ColumnName; + keyRef.Fields.Add(field); + } + keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute)); + elem.Constraints.Add (keyRef); + } + } private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table) { @@ -1096,58 +1424,100 @@ 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 // XmlSchemaSequence seq = new XmlSchemaSequence (); complex.Particle = seq; - foreach( DataColumn col in elements ) - { - // + foreach (DataColumn col in elements) { + + // 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); + } + + if (col.AutoIncrementSeed != 0) { + xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace); + xattr.Value = col.AutoIncrementSeed.ToString(); + xattrs.Add (xattr); + } - colElem.SchemaTypeName = MapType (col.DataType); + 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 ) - { + if (col.AllowDBNull) { colElem.MinOccurs = 0; } - //writer.WriteAttributeString( XmlConstants.MsdataPrefix, - // XmlConstants.Ordinal, - // XmlConstants.MsdataNamespace, - // col.Ordinal.ToString() ); + //writer.WriteAttributeString (XmlConstants.MsdataPrefix, + // XmlConstants.Ordinal, + // XmlConstants.MsdataNamespace, + // col.Ordinal.ToString ()); // Write SimpleType if column have MaxLength - if (col.MaxLength > -1) - { + if (col.MaxLength > -1) { colElem.SchemaType = GetTableSimpleType (doc, col); } - + + colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute)); seq.Items.Add (colElem); } + + foreach (DataRelation rel in table.ChildRelations) { + if (rel.Nested) { + seq.Items.Add(GetTableSchema (doc, rel.ChildTable)); + } + } } //Then a list of attributes - foreach( DataColumn col in atts ) - { + foreach (DataColumn col in atts) { // XmlSchemaAttribute att = new XmlSchemaAttribute (); att.Name = col.ColumnName; @@ -1155,6 +1525,7 @@ namespace System.Data { att.SchemaTypeName = MapType (col.DataType); complex.Attributes.Add (att); } + return elem; } @@ -1171,45 +1542,43 @@ 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 ) + private void DoWriteXmlSchema (XmlWriter writer) { - GetSchemaSerializable ().Write (writer); + BuildSchema ().Write (writer); } /// /// Helper function to split columns into attributes elements and simple /// content /// - private void SplitColumns( DataTable table, - out ArrayList atts, - out ArrayList elements, - out DataColumn simple) + private void SplitColumns (DataTable table, + 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 - atts = new System.Collections.ArrayList(); - elements = new System.Collections.ArrayList(); + atts = new System.Collections.ArrayList (); + elements = new System.Collections.ArrayList (); simple = null; //Sort out the columns - foreach( DataColumn col in table.Columns ) - { - switch( col.ColumnMapping ) - { + foreach (DataColumn col in table.Columns) { + switch (col.ColumnMapping) { case MappingType.Attribute: - atts.Add( col ); + atts.Add (col); break; case MappingType.Element: - elements.Add( col ); + elements.Add (col); break; case MappingType.SimpleContent: - if( simple != null ) - { - throw new System.InvalidOperationException( "There may only be one simple content element" ); + if (simple != null) { + throw new System.InvalidOperationException ("There may only be one simple content element"); } simple = col; break; @@ -1219,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; @@ -1241,11 +1631,16 @@ namespace System.Data { case TypeCode.UInt64: return XmlConstants.QnUnsignedLong; } - if (typeof (TimeSpan) == type) return XmlConstants.QnDuration; - else if (typeof (System.Uri) == type) return XmlConstants.QnUri; - else if (typeof (byte[]) == type) return XmlConstants.QnBase64Binary; - else if (typeof (XmlQualifiedName) == type) return XmlConstants.QnXmlQualifiedName; - else return XmlConstants.QnString; + if (typeof (TimeSpan) == type) + return XmlConstants.QnDuration; + else if (typeof (System.Uri) == type) + return XmlConstants.QnUri; + else if (typeof (byte[]) == type) + return XmlConstants.QnBase64Binary; + else if (typeof (XmlQualifiedName) == type) + return XmlConstants.QnXmlQualifiedName; + else + return XmlConstants.QnString; } #endregion //Private Xml Serialisation