2012-06-15 3 views
0

En guise d'arrière-plan à cette question, je veux lier un "code fait" DataTable à un . Pour conserver cette table, j'implémente l'interface ISerializable. La table est affichée correctement, mais en postback pour trier les lignes, un InvalidCastExcpetion est levé car les éléments de la ligne.ItemArray sont passés du type double au type string.DataTable sous-classé modifie les informations DataType de Double à Chaîne lors de la sérialisation/désérialisation

Ceci est la classe. Remarque: deux constructeurs, l'un pour la construction du DataTable initialement et un pour la construction de la table dans le processus de désérialisation:

[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); 
     } 
    } 

Comme je peux observer en lisant le schéma dans le SerializationInfo dans le constructeur HeatMapVisualisationDataTable(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext ctxt), la table est correcte sérialisé et vient correctement du "magasin" en termes de schéma xml serilaised. Cependant, en regardant la classe de base construite dans ce constructeur, toutes les colonnes de type double avant la sérialisation sont maintenant de type string. Qu'est-ce que je fais mal?

Mise à jour 1: Je pourrais aussi reproduire ce problème en changeant le type de données numérique de double à float ou decimal. La colonne de clé primaire de type Guid est désérialisée dans le type correct.

Mise à jour 2: À mon avis, le comportement décrit est un suivi de certains problèmes DataTable.ReadXml() où DataTypes de colonnes est remplacé par String.

Merci, Jan

+0

Ma publication est liée à cette [question] (http://stackoverflow.com/questions/7041036/does-dataset-readxml-set-all-columns-datatype-in-its-table-as-string). Cependant, la réponse n'est pas applicable dans mon cas. – jahu

+0

En outre, le code source fourni en bas de cette [page Méthode DataSet.ReadXml] (http://msdn.microsoft.com/en-us/library/d6swf149.aspx) assez bien illustre le problème: 'DataSet.ReadXml () 'définit toutes les colonnes du type de données dans la table comme' string', bien que déclaré différemment. Dans l'exemple MSDN donné, la colonne "id" est déclarée comme type "int" mais après avoir relu la table, la colonne est tapée comme "string". – jahu

Répondre

0

Comme une sorte de solution par circumwenting sérialisation XML de DataTable dans et par la suite la recréation du DataTable de XML à l'aide ReadXml, j'ai utilisé cette MSDN article on Binary Serialization of ADO.NET Objects. L'astuce consiste à stocker les informations sur les noms de colonnes et les types de colonnes dans quelques ArrayList supplémentaires et les mettre dans la sérialisation. Voir ici mes implémentations actuelles de l'interface ISerializable:

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); 
} 

Remarque, je ne l'appelle pas GetObjectData(...) de DataTable et constructeur pour désérialisation plus. Cependant, ce n'est pas la réponse à mon message original. C'est juste une solution de contournement ... Donc la question "Pourquoi ReadXml() change les DataTypes?" est toujours ouvert!

Questions connexes