using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; using System.Data; using System.IO; using System.Linq; using System.Collections; using EasyDevCore.Common; namespace EasyDevCore.Database.EntityTable { /// /// /// public enum DataTableSerializerSchema { /// /// The none /// None, /// /// The data table schema /// DataTableSchema, /// /// The column schema /// ColumnSchema } /// /// Convert DataTable to XML /// [Serializable] public class DataTableSerializer : IXmlSerializable { private DataTable _Table; private DataTableSerializerSchema _ProxySchema = DataTableSerializerSchema.None; private bool _ForSynchValues = false; private bool _OptimumSize = true; private string _KeyFields = string.Empty; private string _ColumnNames = string.Empty; private string _ExceptColumnNames = string.Empty; /// /// use for XmlSerialization /// private DataTableSerializer() { } /// /// Initializes a new instance of the class. /// /// The entities. /// if set to true [only changed values has been serialize for synchronized]. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The key fields. /// The proxy schema. /// if set to true [optimum size but slower]. public DataTableSerializer(DataTable entities, bool forSynchValues = false, string columnNames = "", string exceptColumnNames = "", string keyFields = "", DataTableSerializerSchema proxySchema = DataTableSerializerSchema.None, bool optimumSize = true) { _Table = entities; _ForSynchValues = forSynchValues; _ColumnNames = columnNames; _ExceptColumnNames = exceptColumnNames; _KeyFields = keyFields; _OptimumSize = optimumSize; _ProxySchema = proxySchema; if (proxySchema == DataTableSerializerSchema.None && entities != null) { _ProxySchema = DataTableSerializerSchema.DataTableSchema; } } /// /// Gets the table. /// /// The entities. public virtual DataTable Table { get { return _Table; } } #region IXmlSerializable Members /// /// This property is reserved, apply the to the class instead. /// /// /// An that describes the XML representation of the object that is produced by the method and consumed by the method. /// public System.Xml.Schema.XmlSchema GetSchema() { return null; } /// /// Gets the type of the entity collection. /// /// public virtual Type GetDataTableType() { return null; } /// /// Loads the XML only for changes. /// /// The reader. /// if set to true [optimum size]. /// The map columns. private void LoadXMLOnlyForSync(System.Xml.XmlReader reader, bool optimumSize, Dictionary mapColumns) { DataRow row; DataColumn column; string rowState; while (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "R") { row = Table.NewRow(); rowState = reader.GetAttribute("rowStated"); reader.ReadStartElement(); while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement) { if (reader.LocalName == "CS") //Special columns { string[] colNuls = reader.GetAttribute("N").SplitCommaString(); foreach (string col in colNuls) { column = Table.Columns[mapColumns["C" + col]]; row[column.Ordinal] = DBNull.Value; } if (optimumSize) { string[] colZoEs = reader.GetAttribute("E").SplitCommaString(); foreach (string col in colZoEs) { column = Table.Columns[mapColumns["C" + col]]; row[column.Ordinal] = GetZeroOrEmptyValue(column.DataType); } } reader.Read(); } else { column = Table.Columns[mapColumns[reader.LocalName]]; row[column.Ordinal] = reader.ReadElementString().StringTo(column.DataType); } } Table.Rows.Add(row); switch (rowState) { case "Modified": row.AcceptChanges(); row.SetModified(); break; case "Deleted": row.Delete(); break; } reader.ReadEndElement(); } } /// /// Loads the XML all rows. /// /// The reader. /// if set to true [optimum size]. /// The map columns. private void LoadXMLAllRows(System.Xml.XmlReader reader, bool optimumSize, Dictionary mapColumns) { DataRow row; DataColumn column; while (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "R") { row = Table.NewRow(); reader.ReadStartElement(); while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement) { if (reader.LocalName == "CS") //Special columns { string colNuls = reader.GetAttribute("N"); if (!string.IsNullOrWhiteSpace(colNuls)) { foreach (string col in colNuls.SplitCommaString()) { column = Table.Columns[mapColumns["C" + col]]; row[column.Ordinal] = DBNull.Value; } } if (optimumSize) { string colZoEs = reader.GetAttribute("E"); if (!string.IsNullOrWhiteSpace(colZoEs)) { foreach (string col in colZoEs.SplitCommaString()) { column = Table.Columns[mapColumns["C" + col]]; row[column.Ordinal] = GetZeroOrEmptyValue(column.DataType); } } } reader.Read(); } else { column = Table.Columns[mapColumns[reader.LocalName]]; row[column.Ordinal] = reader.ReadElementString().StringTo(column.DataType); } } Table.Rows.Add(row); reader.ReadEndElement(); } Table.AcceptChanges(); } /// /// Generates an object from its XML representation. /// /// The stream from which the object is deserialized. public void ReadXml(System.Xml.XmlReader reader) { bool forSynchValues = false; bool includeSchema = false; string type = string.Empty; string name = string.Empty; DataColumnCollection schemaColumns = null; reader.Read(); if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Option") { type = reader.GetAttribute("type"); name = reader.GetAttribute("name"); forSynchValues = reader.GetAttribute("forSynchValues").StringTo(false); includeSchema = reader.GetAttribute("includeSchema").StringTo(false); if (Table == null) { if (string.IsNullOrEmpty(type) && GetDataTableType() != null) { type = GetDataTableType().AssemblyQualifiedName; } if (string.IsNullOrEmpty(type)) { type = typeof(DataTable).AssemblyQualifiedName; } _Table = (DataTable)ReflectionHelper.CreateInstance(type); if (!string.IsNullOrWhiteSpace(name)) _Table.TableName = name; } else { Table.Clear(); } string[] extendedProperties = reader.GetAttribute("extendedProperties").Split(new char[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < extendedProperties.Length; i = i + 2) { string attributeName = "extP_" + (i / 2).ToString(); string value = reader.GetAttribute(attributeName); string typeName = extendedProperties[i + 1]; string propertyName = extendedProperties[i]; Table.ExtendedProperties[propertyName] = value.StringTo(extendedProperties[i + 1]); } reader.Read(); } schemaColumns = Table.Columns; if (includeSchema) { if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Schema") { string schemaType = reader.GetAttribute("type"); if (schemaType == DataTableSerializerSchema.ColumnSchema.ToString()) { reader.ReadStartElement(); while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement) { DataColumn col = Table.Columns.Add(reader.LocalName, Type.GetType(reader.GetAttribute("type"))); string defaultValue = reader.GetAttribute("defaultValue"); col.DefaultValue = defaultValue == "" ? DBNull.Value : defaultValue.StringTo(col.DataType); reader.Read(); } reader.ReadEndElement(); } else //DataTable schema { string schema = reader.ReadInnerXml(); if (!string.IsNullOrEmpty(schema)) { using (StringReader sr = new StringReader(schema)) { DataTable tempTable = new DataTable(name ?? "DataTable"); //Use only for get schema tempTable.ReadXmlSchema(sr); schemaColumns = tempTable.Columns; } using (StringReader sr = new StringReader(schema)) { Table.ReadXmlSchema(sr); } } } } } Dictionary mapColumns = new Dictionary(); foreach (DataColumn column in schemaColumns) { mapColumns.Add(string.Format("C{0}", column.Ordinal), Table.Columns[column.ColumnName].Ordinal); } if (forSynchValues) { LoadXMLOnlyForSync(reader, _OptimumSize, mapColumns); } else { LoadXMLAllRows(reader, _OptimumSize, mapColumns); } reader.ReadEndElement(); } /// /// Determines whether [is zero or empty] [the specified value]. /// /// The value. /// /// true if [is zero or empty] [the specified value]; otherwise, false. /// protected bool IsZeroOrEmpty(object value) { Type type = value.GetType(); if (type == typeof(string)) { return (string)value == string.Empty; } else { if (type.IsNumericType()) { return value.Equals(0); } } return false; } /// /// Gets the zero or empty value. /// /// The type. /// protected object GetZeroOrEmptyValue(Type type) { if (type == typeof(string)) { return string.Empty; } else { if (type.IsNumericType()) { return 0; } } return string.Empty; } /// /// Creates the XML only for sync values. /// /// The writer. private void CreateXMLOnlyForSync(System.Xml.XmlWriter writer) { string[] exceptColNames = _ExceptColumnNames.SplitCommaString(trimSpace: true); string[] colNames = _ColumnNames.SplitCommaString(trimSpace: true); string[] keyFields = _KeyFields.SplitCommaString(trimSpace: true); Dictionary columns = new Dictionary(); Dictionary modifiedColumns = new Dictionary(); Dictionary primaryKeysColumns = new Dictionary(); foreach (DataColumn column in Table.Columns) { if (string.IsNullOrEmpty(column.Expression) && !column.ReadOnly && (colNames.Length == 0 || Array.IndexOf(colNames, column.ColumnName) > -1) && (exceptColNames.Length == 0 || Array.IndexOf(exceptColNames, column.ColumnName) == -1)) { string colName = string.Format("C{0}", column.Ordinal); columns.Add(column.Ordinal, colName); } } foreach (string columnName in keyFields) { int colNo = Table.Columns[columnName].Ordinal; string colName = string.Format("C{0}", Table.Columns[columnName].Ordinal); primaryKeysColumns.Add(colNo, colName); modifiedColumns.Add(colNo, colName); } DataView dataView = new DataView(Table, "", "", DataViewRowState.Added | DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent); DataRow entity; foreach (DataRowView rowView in dataView) { entity = rowView.Row; writer.WriteStartElement("R"); writer.WriteAttributeString("rowStated", entity.RowState.ToString()); string colNuls = string.Empty; string colZeroOrEmpty = string.Empty; switch (entity.RowState) { case DataRowState.Added: foreach (KeyValuePair column in columns) { if (!entity.IsNull(column.Key)) { if (_OptimumSize && IsZeroOrEmpty(entity[column.Key])) { colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString(); } else { writer.WriteElementString(column.Value, entity[column.Key].StringFrom()); } } else { colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString(); } } break; case DataRowState.Deleted: foreach (KeyValuePair column in primaryKeysColumns) { writer.WriteElementString(column.Value, entity[column.Key, DataRowVersion.Original].StringFrom()); } break; case DataRowState.Modified: //Saves only columns has value change if (!entity.IsChanged()) { continue; } foreach (KeyValuePair column in columns) { if (entity.IsChanged(column.Key)) { if (!entity.IsNull(column.Key)) { if (_OptimumSize && IsZeroOrEmpty(entity[column.Key])) { colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString(); } else { writer.WriteElementString(column.Value, entity[column.Key].StringFrom()); } } else { colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString(); } } } break; } bool isNull = !string.IsNullOrWhiteSpace(colNuls); bool isZeroOrEmpty = !string.IsNullOrWhiteSpace(colZeroOrEmpty); if (isNull || isZeroOrEmpty) { writer.WriteStartElement("CS"); if (isNull) writer.WriteAttributeString("N", colNuls); if (isZeroOrEmpty) writer.WriteAttributeString("E", colZeroOrEmpty); writer.WriteEndElement(); } writer.WriteEndElement(); } } /// /// Creates the XML all rows. /// /// The writer. private void CreateXMLAllRows(System.Xml.XmlWriter writer) { string[] exceptColNames = _ExceptColumnNames.SplitCommaString(trimSpace: true); string[] colNames = _ColumnNames.SplitCommaString(trimSpace: true); DataView dataView = new DataView(Table, "", "", DataViewRowState.CurrentRows); DataRow entity; Dictionary columns = new Dictionary(); foreach (DataColumn column in Table.Columns) { if (string.IsNullOrEmpty(column.Expression) && !column.ReadOnly && (colNames.Length == 0 || Array.IndexOf(colNames, column.ColumnName) > -1) && (exceptColNames.Length == 0 || Array.IndexOf(exceptColNames, column.ColumnName) == -1)) { columns.Add(column.Ordinal, string.Format("C{0}", column.Ordinal)); } } foreach (DataRowView rowView in dataView) { entity = rowView.Row; writer.WriteStartElement("R"); string colNuls = string.Empty; string colZeroOrEmpty = string.Empty; foreach (KeyValuePair column in columns) { if (!entity.IsNull(column.Key)) { if (_OptimumSize && IsZeroOrEmpty(entity[column.Key])) { colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString(); } else { writer.WriteElementString(column.Value, entity[column.Key].StringFrom()); } } else { colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString(); } } bool isNull = !string.IsNullOrWhiteSpace(colNuls); bool isZeroOrEmpty = !string.IsNullOrWhiteSpace(colZeroOrEmpty); if (isNull || isZeroOrEmpty) { writer.WriteStartElement("CS"); if (isNull) writer.WriteAttributeString("N", colNuls); if (isZeroOrEmpty) writer.WriteAttributeString("E", colZeroOrEmpty); writer.WriteEndElement(); } writer.WriteEndElement(); } } /// /// Converts an object into its XML representation. /// /// The stream to which the object is serialized. public void WriteXml(System.Xml.XmlWriter writer) { writer.WriteStartElement("Option"); writer.WriteAttributeString("name", Table.TableName); writer.WriteAttributeString("includeSchema", (_ProxySchema != DataTableSerializerSchema.None).ToString()); writer.WriteAttributeString("type", _ProxySchema != DataTableSerializerSchema.None ? string.Empty : Table.GetType().AssemblyQualifiedName); writer.WriteAttributeString("forSynchValues", _ForSynchValues.ToString()); writer.WriteAttributeString("optimumSize", _OptimumSize.ToString()); string extendedProperites = string.Empty; int p = 0; foreach (DictionaryEntry prop in Table.ExtendedProperties) { extendedProperites += (!string.IsNullOrWhiteSpace(extendedProperites) ? "," : string.Empty) + prop.Key + "|" + prop.Value.GetType().FullName; writer.WriteAttributeString("extP_" + p++.ToString(), prop.Value.StringFrom()); } writer.WriteAttributeString("extendedProperties", extendedProperites); writer.WriteEndElement(); if (_ProxySchema != DataTableSerializerSchema.None) { writer.WriteStartElement("Schema"); if (_ProxySchema == DataTableSerializerSchema.DataTableSchema) { Table.WriteXmlSchema(writer); } if (_ProxySchema == DataTableSerializerSchema.ColumnSchema) { writer.WriteAttributeString("type", _ProxySchema.ToString()); foreach (DataColumn col in Table.Columns) { writer.WriteStartElement(col.ColumnName); writer.WriteAttributeString("type", col.DataType.FullName); writer.WriteAttributeString("defaultValue", col.DefaultValue == DBNull.Value ? "" : col.DefaultValue.ToString()); writer.WriteEndElement(); } } writer.WriteEndElement(); } if (_ForSynchValues) { CreateXMLOnlyForSync(writer); } else { CreateXMLAllRows(writer); } } #endregion } }