// // System.Data/DataSet.cs // // Author: // Christopher Podurgiel // Daniel Morgan // Rodrigo Moya // Stuart Caborn // Tim Coleman (tim@timcoleman.com) // Ville Palo // // (C) Ximian, Inc. 2002 // Copyright (C) Tim Coleman, 2002 // using System; using System.Collections; using System.ComponentModel; using System.Globalization; using System.Threading; using System.IO; using System.Runtime.Serialization; using System.Xml; using System.Xml.Schema; 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 { private string dataSetName; private string _namespace = ""; private string prefix; private bool caseSensitive; private bool enforceConstraints = true; private DataTableCollection tableCollection; private DataRelationCollection relationCollection; private PropertyCollection properties; private DataViewManager defaultView; private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture; #region Constructors public DataSet() : this ("NewDataSet") { } public DataSet(string name) { dataSetName = name; tableCollection = new DataTableCollection (this); relationCollection = new DataRelationCollection.DataSetRelationCollection (this); properties = new PropertyCollection(); this.prefix = String.Empty; this.Locale = CultureInfo.CurrentCulture; } [MonoTODO] protected DataSet(SerializationInfo info, StreamingContext context) : this () { throw new NotImplementedException (); } #endregion // Constructors #region Public Properties [DataCategory ("Data")] [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")] [DefaultValue (false)] public bool 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(); } } } } [DataCategory ("Data")] [DataSysDescription ("The name of this DataSet.")] [DefaultValue ("")] public string DataSetName { get { return dataSetName; } set { dataSetName = value; } } [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")] [Browsable (false)] public DataViewManager DefaultViewManager { get { if (defaultView == null) defaultView = new DataViewManager (this); return defaultView; } } [DataSysDescription ("Indicates whether constraint rules are to be followed.")] [DefaultValue (true)] public bool EnforceConstraints { get { return enforceConstraints; } set { if (value != enforceConstraints) { enforceConstraints = value; if (value) { foreach (DataTable table in Tables) { foreach (Constraint c in table.Constraints) c.AssertConstraint(); } } } } } [Browsable (false)] [DataCategory ("Data")] [DataSysDescription ("The collection that holds custom user information.")] public PropertyCollection ExtendedProperties { get { return properties; } } [Browsable (false)] [DataSysDescription ("Indicates that the DataSet has errors.")] public bool HasErrors { [MonoTODO] get { for (int i = 0; i < Tables.Count; i++) { if (Tables[i].HasErrors) return true; } return false; } } [DataCategory ("Data")] [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")] public CultureInfo Locale { get { return locale; } set { if (locale == null || !locale.Equals(value)) { // TODO: check if the new locale is valid // TODO: update locale of all tables locale = value; } } } public void Merge (DataRow[] rows) { Merge (rows, false, MissingSchemaAction.Add); } public void Merge (DataSet dataSet) { Merge (dataSet, false, MissingSchemaAction.Add); } public void Merge (DataTable table) { Merge (table, false, MissingSchemaAction.Add); } public void Merge (DataSet dataSet, bool preserveChanges) { Merge (dataSet, preserveChanges, MissingSchemaAction.Add); } [MonoTODO] public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction) { if(rows == null) throw new ArgumentNullException("rows"); if(!IsLegalSchemaAction(missingSchemaAction)) throw new ArgumentOutOfRangeException("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"); 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"); MergeManager.Merge(this, table, preserveChanges, missingSchemaAction); } private static bool IsLegalSchemaAction(MissingSchemaAction missingSchemaAction) { if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore) return true; return false; } [DataCategory ("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; } } [DataCategory ("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? if (value == null) { value = string.Empty; } if (value != this.prefix) { RaisePropertyChanging("Prefix"); } prefix = value; } } [DataCategory ("Data")] [DataSysDescription ("The collection that holds the relations for this DatSet.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] public DataRelationCollection Relations { get { return relationCollection; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public override ISite Site { [MonoTODO] get { throw new NotImplementedException (); } [MonoTODO] set { throw new NotImplementedException (); } } [DataCategory ("Data")] [DataSysDescription ("The collection that holds the tables for this DataSet.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] public DataTableCollection Tables { get { return tableCollection; } } #endregion // Public Properties #region Public Methods [MonoTODO] public void AcceptChanges() { foreach (DataTable tempTable in tableCollection) tempTable.AcceptChanges (); } public void Clear() { // TODO: if currently bound to a XmlDataDocument // throw a NotSupportedException for (int t = 0; t < tableCollection.Count; t++) { tableCollection[t].Clear (); } } public virtual DataSet Clone() { DataSet Copy = new DataSet (); CopyProperties (Copy); foreach (DataTable Table in Tables) { Copy.Tables.Add (Table.Clone ()); } //Copy Relationships between tables after existance of tables //and setting properties correctly CopyRelations(Copy); return Copy; } // Copies both the structure and data for this DataSet. public DataSet Copy() { DataSet Copy = new DataSet (); CopyProperties (Copy); // Copy DatSet's 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); return Copy; } [MonoTODO] private void CopyProperties (DataSet Copy) { Copy.CaseSensitive = CaseSensitive; //Copy.Container = Container Copy.DataSetName = DataSetName; //Copy.DefaultViewManager //Copy.DesignMode Copy.EnforceConstraints = EnforceConstraints; //Copy.ExtendedProperties //Copy.HasErrors //Copy.Locale = Locale; Copy.Namespace = Namespace; Copy.Prefix = Prefix; //Copy.Site = Site; } 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 //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); } } public DataSet GetChanges() { return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); } public DataSet GetChanges(DataRowState rowStates) { if(!HasChanges(rowStates)) return null; DataSet copySet = Clone(); IEnumerator tableEnumerator = Tables.GetEnumerator(); DataTable origTable; DataTable copyTable; while (tableEnumerator.MoveNext()) { origTable = (DataTable)tableEnumerator.Current; copyTable = copySet.Tables[origTable.TableName]; 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); } } } return copySet; } public string GetXml() { StringWriter Writer = new StringWriter (); // Sending false for not printing the Processing instruction WriteXml (Writer, XmlWriteMode.IgnoreSchema,false); return Writer.ToString (); } public string GetXmlSchema() { StringWriter Writer = new StringWriter (); WriteXmlSchema (Writer); return Writer.ToString (); } [MonoTODO] public bool HasChanges() { return HasChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified); } [MonoTODO] public bool HasChanges(DataRowState 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++) { table = tableCollection[i]; rowCollection = table.Rows; for (int j = 0; j < rowCollection.Count; j++) { row = rowCollection[j]; if((row.RowState & rowState) != 0) return true; } } return false; } [MonoTODO] public void InferXmlSchema(XmlReader reader, string[] nsArray) { } public void InferXmlSchema(Stream stream, string[] nsArray) { InferXmlSchema (new XmlTextReader(stream), nsArray); } public void InferXmlSchema(TextReader reader, string[] nsArray) { InferXmlSchema (new XmlTextReader(reader), nsArray); } public void InferXmlSchema(string fileName, string[] nsArray) { XmlTextReader reader = new XmlTextReader(fileName); try { InferXmlSchema (reader, nsArray); } finally { reader.Close (); } } public virtual void RejectChanges() { int i; bool oldEnforceConstraints = this.EnforceConstraints; this.EnforceConstraints = false; for (i=0;(i /// Writes the current data for the DataSet to the specified file. /// /// Fully qualified filename to write to public void WriteXml(string fileName) { XmlWriter writer = new XmlTextWriter(fileName, null ); WriteXml( writer ); writer.Close(); } public void WriteXml(TextWriter writer) { XmlWriter xwriter = new XmlTextWriter(writer ); WriteXml( xwriter ); } public void WriteXml(XmlWriter writer) { WriteXml( writer, XmlWriteMode.IgnoreSchema,false); } public void WriteXml(Stream stream, XmlWriteMode mode,bool writePI) { XmlWriter writer = new XmlTextWriter(stream, null ); WriteXml( writer, mode, writePI ); } public void WriteXml(string fileName, XmlWriteMode mode, bool writePI) { XmlWriter writer = new XmlTextWriter(fileName, null ); WriteXml( writer, mode, writePI ); writer.Close(); } public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writePI) { XmlWriter xwriter = new XmlTextWriter(writer); WriteXml( xwriter, mode, writePI); } public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writePI) { if (writePI && (writer.WriteState == WriteState.Start)) writer.WriteStartDocument (true); ((XmlTextWriter)writer).Formatting = Formatting.Indented; WriteStartElement( writer, mode, Namespace, Prefix, DataSetName ); 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 ); } } writer.WriteEndElement(); } public void WriteXmlSchema(Stream stream) { XmlWriter writer = new XmlTextWriter(stream, null ); WriteXmlSchema( writer ); } public void WriteXmlSchema(string fileName) { XmlWriter writer = new XmlTextWriter( fileName, null ); WriteXmlSchema( writer ); } public void WriteXmlSchema(TextWriter writer) { XmlWriter xwriter = new XmlTextWriter( writer ); 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) { XmlReader reader = new XmlTextReader( stream, null ); ReadXmlSchema( reader); } public void ReadXmlSchema(string str) { XmlReader reader = new XmlTextReader( str ); ReadXmlSchema( reader ); } public void ReadXmlSchema(TextReader treader) { XmlReader reader = new XmlTextReader( treader ); ReadXmlSchema( reader ); } public void ReadXmlSchema(XmlReader reader) { XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this); SchemaMapper.Read (reader); } public XmlReadMode ReadXml (Stream stream) { return ReadXml (new XmlTextReader (stream)); } public XmlReadMode ReadXml (string str) { return ReadXml (new XmlTextReader (str)); } public XmlReadMode ReadXml (TextReader reader) { return ReadXml (new XmlTextReader (reader)); } 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); } public XmlReadMode ReadXml (Stream stream, XmlReadMode mode) { return ReadXml (new XmlTextReader (stream), mode); } public XmlReadMode ReadXml (string str, XmlReadMode mode) { return ReadXml (new XmlTextReader (str), mode); } public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode) { return ReadXml (new XmlTextReader (reader), mode); } [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; } else { XmlDataLoader Loader = new XmlDataLoader (this); Result = Loader.LoadData (reader, mode); } return Result; } #endregion // Public Methods #region Public Events [DataCategory ("Action")] [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")] public event MergeFailedEventHandler MergeFailed; #endregion // Public Events #region Destructors ~DataSet() { } #endregion Destructors #region IListSource methods IList IListSource.GetList () { return DefaultViewManager; } bool IListSource.ContainsListCollection { get { return true; } } #endregion IListSource methods #region ISupportInitialize methods public void BeginInit () { } public void EndInit () { } #endregion #region ISerializable void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc) { throw new NotImplementedException (); } #endregion #region Protected Methods 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))); } protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable() { return BuildSchema (); } protected virtual void ReadXmlSerializable(XmlReader reader) { ReadXml(reader, XmlReadMode.DiffGram); // FIXME } void IXmlSerializable.ReadXml(XmlReader reader) { reader.MoveToContent (); reader.ReadStartElement (); // reader.MoveToContent (); ReadXmlSchema (reader); reader.MoveToContent (); ReadXml(reader, XmlReadMode.IgnoreSchema); reader.MoveToContent (); reader.ReadEndElement (); // } void IXmlSerializable.WriteXml(XmlWriter writer) { DoWriteXmlSchema (writer); WriteXml(writer, XmlWriteMode.IgnoreSchema, true); } protected virtual bool ShouldSerializeRelations () { return true; } protected virtual bool ShouldSerializeTables () { return true; } [MonoTODO] protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) { } [MonoTODO] protected virtual void OnRemoveRelation (DataRelation relation) { } [MonoTODO] protected virtual void OnRemoveTable (DataTable table) { } protected internal virtual void OnMergeFailed (MergeFailedEventArgs e) { if (MergeFailed != null) MergeFailed(this, e); } [MonoTODO] protected internal void RaisePropertyChanging (string name) { } #endregion #region Private Xml Serialisation 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); } if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o); if (o is Guid) return XmlConvert.ToString ((Guid) o); return o.ToString(); } private void WriteTable( XmlWriter writer, DataTable table, XmlWriteMode mode ) { DataRow[] rows = new DataRow [table.Rows.Count]; table.Rows.CopyTo (rows, 0); WriteTable (writer, rows, mode); } private void WriteTable( XmlWriter writer, DataRow[] rows, XmlWriteMode mode ) { //The columns can be attributes, hidden, elements, or simple content //There can be 0-1 simple content cols or 0-* elements System.Collections.ArrayList atts; System.Collections.ArrayList elements; DataColumn simple = null; 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; // 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) { AllNulls = false; break; } } // If all of the columns were null, we have to write empty element if (AllNulls) { 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() ); } if( simple != null ) { writer.WriteString( WriteObjectXml(row[simple]) ); } 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(); } } foreach (DataRelation relation in table.ChildRelations) { if (relation.Nested) { WriteTable (writer, row.GetChildRows(relation), mode); } } writer.WriteEndElement(); } } 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 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; }; } XmlSchema IXmlSerializable.GetSchema() { return BuildSchema (); } XmlSchema BuildSchema() { XmlSchema schema = new XmlSchema (); schema.AttributeFormDefault = XmlSchemaForm.Qualified; XmlSchemaElement elem = new XmlSchemaElement (); elem.Name = 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); XmlSchemaComplexType complex = new XmlSchemaComplexType (); elem.SchemaType = complex; XmlSchemaChoice choice = new XmlSchemaChoice (); 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 ) { bool isTopLevel = true; foreach( DataRelation rel in table.ParentRelations ) { if( rel.Nested ) { isTopLevel = false; break; } } if( isTopLevel ) { choice.Items.Add (GetTableSchema (doc, table)); } } //TODO - now add in the relationships as key and unique constraints etc return schema; } private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table) { ArrayList elements; ArrayList atts; DataColumn simple; SplitColumns (table, out atts, out elements, out simple); XmlSchemaElement elem = new XmlSchemaElement (); elem.Name = table.TableName; XmlSchemaComplexType complex = new XmlSchemaComplexType (); elem.SchemaType = complex; //TODO - what about the simple content? if( elements.Count == 0 ) { } else { //A sequence of element types or a simple content node // XmlSchemaSequence seq = new XmlSchemaSequence (); complex.Particle = seq; foreach( DataColumn col in elements ) { // XmlSchemaElement colElem = new XmlSchemaElement (); 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.DefaultValue.ToString () != string.Empty) colElem.DefaultValue = col.DefaultValue.ToString (); colElem.SchemaTypeName = MapType (col.DataType); if( col.AllowDBNull ) { colElem.MinOccurs = 0; } //writer.WriteAttributeString( XmlConstants.MsdataPrefix, // XmlConstants.Ordinal, // XmlConstants.MsdataNamespace, // col.Ordinal.ToString() ); // Write SimpleType if column have MaxLength if (col.MaxLength > -1) { colElem.SchemaType = GetTableSimpleType (doc, col); } seq.Items.Add (colElem); } } //Then a list of attributes foreach( DataColumn col in atts ) { // XmlSchemaAttribute att = new XmlSchemaAttribute (); att.Name = col.ColumnName; att.Form = XmlSchemaForm.Unqualified; att.SchemaTypeName = MapType (col.DataType); complex.Attributes.Add (att); } return elem; } private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col) { // SimpleType XmlSchemaSimpleType simple = new XmlSchemaSimpleType (); // Restriction XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction (); restriction.BaseTypeName = MapType (col.DataType); // MaxValue XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet (); max.Value = XmlConvert.ToString (col.MaxLength); restriction.Facets.Add (max); return simple; } private void DoWriteXmlSchema( XmlWriter writer ) { GetSchemaSerializable ().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) { //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(); simple = null; //Sort out the columns foreach( DataColumn col in table.Columns ) { switch( col.ColumnMapping ) { case MappingType.Attribute: atts.Add( col ); break; case MappingType.Element: elements.Add( col ); break; case MappingType.SimpleContent: if( simple != null ) { throw new System.InvalidOperationException( "There may only be one simple content element" ); } simple = col; break; default: //ignore Hidden elements break; } } } private XmlQualifiedName MapType (Type 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.DateTime: return XmlConstants.QnDateTime; case TypeCode.Decimal: return XmlConstants.QnDecimal; case TypeCode.Double: return XmlConstants.QnDouble; case TypeCode.SByte: return XmlConstants.QnSbyte; case TypeCode.Single: return XmlConstants.QnFloat; case TypeCode.UInt16: return XmlConstants.QnUsignedShort; case TypeCode.UInt32: return XmlConstants.QnUnsignedInt; 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; } #endregion //Private Xml Serialisation } }