0

By way of background to this question, I want to bind a "code made" DataTable to an aspx:GridView. To persist this table I implement the ISerializable interface. The table is displayed correct, but in postback for sorting the rows, a InvalidCastExcpetion is thrown because elements in the row.ItemArray changed from type double to type string.

This is the class. Note: two constructors, one for constructing the DataTable initialy and one for constructing the table in the deserialisation process:

   [global::System.Serializable()]
   [global::System.Xml.Serialization.XmlSchemaProviderAttribute("GetTypedTableSchema")]
    public partial class HeatMapVisualisationDataTable : DataTable, System.Runtime.Serialization.ISerializable
    {
        #region members
        public double _valueMin { get; private set; }
        public double _valueMax { get; private set; }
        #endregion membders

        public HeatMapVisualisationDataTable(XemlExperimentHeatMapDataTable data)
            : base("result", "http://gmd.mpimp-golm.mpg.de/HeatMap")
        {
            this.Columns.Add(new DataColumn("metabolite", typeof(Guid)));

            this.Columns.Add(new DataColumn("name", typeof(string)));

            DataColumn[] PrimaryKeyColumns = new DataColumn[1];
            PrimaryKeyColumns[0] = this.Columns["metabolite"];
            this.PrimaryKey = PrimaryKeyColumns;

            SortedDictionary<int, DataColumn> headers = new SortedDictionary<int, DataColumn>();
            foreach (var item in data.AsParallel().AsEnumerable().Select(x => x.ObservationPointId).Distinct().OrderBy(x => x))
            {
                DataColumn dc = this.Columns.Add(item.ToString(), typeof(Double));
                headers.Add(item, dc);
            }

            foreach (var item in data)
            {
                DataRow tmpRow = base.Rows.Find(item.MetaboliteId);
                if (tmpRow == null)
                {
                    tmpRow = base.Rows.Add(new object[] { item.MetaboliteId });
                    tmpRow["name"] = item.MetaboliteName;
                }
                tmpRow[headers[item.ObservationPointId]] = item.value;
            }

            this.AcceptChanges();

            _valueMax = data.AsParallel().AsUnordered().Max(x => x.value);
            _valueMin = data.AsParallel().AsUnordered().Min(x => x.value);
        }

        public HeatMapVisualisationDataTable(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt) :
            base(info, ctxt)
        {
            _valueMin = (double)info.GetValue("valueMin", typeof(double));
            _valueMax = (double)info.GetValue("valueMax", typeof(double));
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt)
        {
            base.GetObjectData(info, ctxt);
            info.AddValue("valueMin", _valueMin);
            info.AddValue("valueMax", _valueMax);
        }
    }

As I can observe by reading the schema in the SerializationInfo in the constructor HeatMapVisualisationDataTable(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt), the table is correct serialised and is comming correct from the "store" in terms of the serilaised xml schema. However, looking at the constructed base class in this constructor, all columns of type double befor serialsation are now of type string. What am I doing wrong?

Update 1: I could also reproduce this issue by changing the the numeric datatype from double to float or decimal. The primary key column of type Guid is deserialised in to the correct type.

Update 2: To my opinion the described behavior is a follow up of some DataTable.ReadXml() issues where DataTypes of columns get replaced by String.

Thanks, Jan

jahu
  • 533
  • 4
  • 15
  • My posting is related to this [question](http://stackoverflow.com/questions/7041036/does-dataset-readxml-set-all-columns-datatype-in-its-table-as-string). However, the answer is not applicable in my case. – jahu Jun 18 '12 at 22:29
  • Also, the source code provided on bottom of this [DataSet.ReadXml Method page](http://msdn.microsoft.com/en-us/library/d6swf149.aspx) pretty nice demonstrates the problem: `DataSet.ReadXml()` set all columns datatype in the table as `string` although declared differently. In the given MSDN example the column "id" is declared as type `int` but after reading the table back in, the column is typed as `string`. – jahu Jun 18 '12 at 22:38

1 Answers1

0

As a kind of solution by circumwenting DataTable's serialisation into XML and subsequently the recreation of the DataTable from XML using ReadXml, I used this MSDN article on Binary Serialization of ADO.NET Objects. The trick is to store the information about column names and column types in some extra ArrayList and put those into the serialisation. See here my current implementations of the ISerializable interface:

public HeatMapVisualisationDataTable(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt)
    base()
{
    _valueMin = info.GetSingle("valueMin");
    _valueMax = info.GetSingle("valueMax");

    System.Collections.ArrayList colNames = (System.Collections.ArrayList)info.GetValue("colNames", typeof(System.Collections.ArrayList));
    System.Collections.ArrayList colTypes = (System.Collections.ArrayList)info.GetValue("colTypes", typeof(System.Collections.ArrayList));
    System.Collections.ArrayList dataRows = (System.Collections.ArrayList)info.GetValue("dataRows", typeof(System.Collections.ArrayList));

    // Add columns
    for (int i = 0; i < colNames.Count; i++)
    {
        DataColumn col = new DataColumn(colNames[i].ToString(), Type.GetType(colTypes[i].ToString()));
        this.Columns.Add(col);
    }

    // Add rows
    for (int i = 0; i < dataRows.Count; i++)
    {
        DataRow row = this.Rows.Add((object[])dataRows[i]);
    }

    this.AcceptChanges();
}

public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt)
{
    System.Collections.ArrayList colNames = new System.Collections.ArrayList();
    System.Collections.ArrayList colTypes = new System.Collections.ArrayList();
    System.Collections.ArrayList dataRows = new System.Collections.ArrayList();

    foreach (DataColumn col in this.Columns)
    {
        colNames.Add(col.ColumnName);
        colTypes.Add(col.DataType.FullName);
    }

    foreach (DataRow row in this.Rows)
    {
        dataRows.Add(row.ItemArray);
    }

    info.AddValue("colNames", colNames);
    info.AddValue("colTypes", colTypes);
    info.AddValue("dataRows", dataRows);
    info.AddValue("valueMin", _valueMin);
    info.AddValue("valueMax", _valueMax);
}

Note, I don't call DataTable's GetObjectData(...) and constructor for deserialisation any more. However, this is not the answer to my original posting. It is just a workaround... So the question "Why does ReadXml() change the DataTypes?" is still open!

BenMorel
  • 34,448
  • 50
  • 182
  • 322
jahu
  • 533
  • 4
  • 15