50 votes

Convertir DataTable en IEnumerable<T>

J'essaie de convertir un DataTable en IEnumerable. Où T est un type personnalisé que j'ai créé. Je sais que je peux le faire en créant un List<T> mais je me demandais s'il n'y avait pas un moyen plus astucieux de le faire en utilisant IEnumerable. Voici ce que j'ai maintenant :

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable)
{
    var tankReadings = new List<TankReading>();
    foreach (DataRow row in dataTable.Rows)
    {
        var tankReading = 
              new TankReading
              {
                  TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),
                  TankID = Convert.ToInt32(row["TankID"]),
                  ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),
                  ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),
                  ReadingInches = Convert.ToInt32(row["ReadingInches"]),
                  MaterialNumber = row["MaterialNumber"].ToString(),
                  EnteredBy = row["EnteredBy"].ToString(),
                  ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),
                  MaterialID = Convert.ToInt32(row["MaterialID"]),
                  Submitted = Convert.ToBoolean(row["Submitted"]),
              };

        tankReadings.Add(tankReading);
    }
    return tankReadings.AsEnumerable();
}

La partie essentielle étant que je crée un List<T> puis le retourner en utilisant AsEnumerable() .

71voto

JaredReisinger Points 2883

Il existe également une méthode DataSetExtension appelée "AsEnumerable()" (dans System.Data) qui prend une DataTable et renvoie un Enumerable. Voir le document MSDN pour plus de détails, mais en gros, c'est aussi simple que ça :

dataTable.AsEnumerable()

L'inconvénient est qu'il s'agit d'une énumération de DataRow, et non de votre classe personnalisée. Un appel LINQ "Select()" pourrait cependant convertir les données de la ligne :

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable)
{
    return dataTable.AsEnumerable().Select(row => new TankReading      
            {      
                TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),      
                TankID = Convert.ToInt32(row["TankID"]),      
                ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),      
                ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),      
                ReadingInches = Convert.ToInt32(row["ReadingInches"]),      
                MaterialNumber = row["MaterialNumber"].ToString(),      
                EnteredBy = row["EnteredBy"].ToString(),      
                ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),      
                MaterialID = Convert.ToInt32(row["MaterialID"]),      
                Submitted = Convert.ToBoolean(row["Submitted"]),      
            });
}

0 votes

Oooh, sympa. Je n'avais pas remarqué dataTable.AsEnumerable() et faisait toujours le plus long, le plus laid : dataTable.Rows.Cast<DataSetName.SomeLongDataTableRowName>()

16 votes

El AsEnumerable La méthode d'extension se trouve dans le System.Data mais veillez à faire référence à l'espace de nom System.Data.DataSetExtensions assemblage pour l'utiliser.

0 votes

Utilisez l'extension .Cast<DataRowView>() pour les vues de données.

55voto

Matt Greer Points 29401

Rien de mal à cette mise en œuvre. Vous pourriez donner à la yield mot-clé un essai, voir comment vous l'aimez :

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable)
    {
        foreach (DataRow row in dataTable.Rows)
        {
            yield return new TankReading
                                  {
                                      TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),
                                      TankID = Convert.ToInt32(row["TankID"]),
                                      ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),
                                      ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),
                                      ReadingInches = Convert.ToInt32(row["ReadingInches"]),
                                      MaterialNumber = row["MaterialNumber"].ToString(),
                                      EnteredBy = row["EnteredBy"].ToString(),
                                      ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),
                                      MaterialID = Convert.ToInt32(row["MaterialID"]),
                                      Submitted = Convert.ToBoolean(row["Submitted"]),
                                  };
        }

    }

De même, le AsEnumerable n'est pas nécessaire, car List<T> est déjà un IEnumerable<T>

1voto

Ben Points 14995

Méthode simple utilisant System.Data.DataSetExtensions :

table.AsEnumerable().Select(row => new TankReading{
        TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),
        TankID = Convert.ToInt32(row["TankID"]),
        ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),
        ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),
        ReadingInches = Convert.ToInt32(row["ReadingInches"]),
        MaterialNumber = row["MaterialNumber"].ToString(),
        EnteredBy = row["EnteredBy"].ToString(),
        ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),
        MaterialID = Convert.ToInt32(row["MaterialID"]),
        Submitted = Convert.ToBoolean(row["Submitted"]),
    });

Ou :

TankReading TankReadingFromDataRow(DataRow row){
    return new TankReading{
        TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),
        TankID = Convert.ToInt32(row["TankID"]),
        ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),
        ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),
        ReadingInches = Convert.ToInt32(row["ReadingInches"]),
        MaterialNumber = row["MaterialNumber"].ToString(),
        EnteredBy = row["EnteredBy"].ToString(),
        ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),
        MaterialID = Convert.ToInt32(row["MaterialID"]),
        Submitted = Convert.ToBoolean(row["Submitted"]),
    };
}

// Now you can do this
table.AsEnumerable().Select(row => return TankReadingFromDataRow(row));

Ou, mieux encore, créez un TankReading(DataRow r) constructeur, alors cela devient :

    table.AsEnumerable().Select(row => return new TankReading(row));

1voto

ErikE Points 18233

Si vous produisez le DataTable à partir d'une requête SQL, avez-vous envisagé d'utiliser simplement Dapper à la place ?

Ensuite, au lieu de faire un SqlCommand avec SqlParameters et un DataTable et un DataAdapter et ainsi de suite, qu'il faut ensuite laborieusement convertir en classe, il suffit de définir la classe, de faire en sorte que les noms des colonnes de la requête correspondent aux noms des champs, et les paramètres sont liés facilement par leur nom. Vous avez déjà le TankReading définie, donc ce sera très simple !

using Dapper;

// Below can be SqlConnection cast to DatabaseConnection, too.
DatabaseConnection connection = // whatever
IEnumerable<TankReading> tankReadings = connection.Query<TankReading>(
   "SELECT * from TankReading WHERE Value = @value",
   new { value = "tank1" } // note how `value` maps to `@value`
);
return tankReadings;

N'est-ce pas génial ? Dapper est très optimisé et vous donnera des performances presque équivalentes à celles de la lecture directe avec un fichier de type DataAdapter .

Si votre classe contient de la logique, si elle est immuable ou si elle n'a pas de constructeur sans paramètre, alors vous avez probablement besoin d'une classe de type DbTankReading (en tant que POCO/Plain Old Class Object pur) :

// internal because it should only be used in the data source project and not elsewhere
internal sealed class DbTankReading {
   int TankReadingsID { get; set; }
   DateTime? ReadingDateTime { get; set; }
   int ReadingFeet { get; set; }
   int ReadingInches { get; set; }
   string MaterialNumber { get; set; }
   string EnteredBy { get; set; }
   decimal ReadingPounds { get; set; }
   int MaterialID { get; set; }
   bool Submitted { get; set; }
}

Tu utiliserais ça comme ça :

IEnumerable<TankReading> tankReadings = connection
   .Query<DbTankReading>(
      "SELECT * from TankReading WHERE Value = @value",
      new { value = "tank1" } // note how `value` maps to `@value`
   )
   .Select(tr => new TankReading(
      tr.TankReadingsID,
      tr.ReadingDateTime,
      tr.ReadingFeet,
      tr.ReadingInches,
      tr.MaterialNumber,
      tr.EnteredBy,
      tr.ReadingPounds,
      tr.MaterialID,
      tr.Submitted
   });

Malgré le travail de cartographie, cette méthode reste moins pénible que celle des tableaux de données. Cette méthode vous permet également d'exécuter une certaine logique, bien que si la logique va au-delà d'une simple mise en correspondance directe, je la placerais dans un fichier séparé, le TankReadingMapper classe.

1voto

Si vous voulez convertir n'importe quelle DataTable en une fonction vectorielle IEnumerable équivalente.

Jetez un coup d'œil à la fonction générique suivante, elle peut répondre à vos besoins (vous devrez peut-être inclure des cas d'écriture pour différents types de données en fonction de vos besoins).

/// <summary>
    /// Get entities from DataTable
    /// </summary>
    /// <typeparam name="T">Type of entity</typeparam>
    /// <param name="dt">DataTable</param>
    /// <returns></returns>
    public IEnumerable<T> GetEntities<T>(DataTable dt)
    {
        if (dt == null)
        {
            return null;
        }

        List<T> returnValue = new List<T>();
        List<string> typeProperties = new List<string>();

        T typeInstance = Activator.CreateInstance<T>();

        foreach (DataColumn column in dt.Columns)
        {
            var prop = typeInstance.GetType().GetProperty(column.ColumnName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            if (prop != null)
            {
                typeProperties.Add(column.ColumnName);
            }
        }

        foreach (DataRow row in dt.Rows)
        {
            T entity = Activator.CreateInstance<T>();

            foreach (var propertyName in typeProperties)
            {

                if (row[propertyName] != DBNull.Value)
                {
                    string str = row[propertyName].GetType().FullName;

                    if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.String))
                    {
                        object Val = row[propertyName].ToString();
                        entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null);
                    }
                    else if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.Guid)) 
                    {
                        object Val = Guid.Parse(row[propertyName].ToString());
                        entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null);
                    }
                    else
                    {
                        entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, row[propertyName], BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null);
                    }
                }
                else
                {
                    entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, null, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null);
                }
            }

            returnValue.Add(entity);
        }

        return returnValue.AsEnumerable();
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X