wake-up-neo.com

Konvertieren von DataTable in generische Liste in C #

Haftungsausschluss: Ich weiß, dass es an so vielen Stellen bei SO gefragt wird.
Meine Frage ist etwas anders. 

Programmiersprache: C # 3.5

Ich habe eine DataTable-Karte namens namedTabelle, die Daten aus der Datenbank abruft, und ich habe eine Klasse Cards, die nur einige Eigenschaften hat (kein Konstruktor)

public class Cards
{
    public Int64 CardID { get; set; }
    public string CardName { get; set; }
    public Int64 ProjectID { get; set; }
    public Double CardWidth { get; set; }
    public Double CardHeight { get; set; }
    public string Orientation { get; set; }
    public string BackgroundImage { get; set; }
    public string Background { get; set; }
}

Ich möchte die cardsTable-Daten in ein Objekt vom Typ List einfügen.
Meine Daten enthalten Nullfelder, und daher sollte die Methode nicht fehlerhaft sein, wenn ich die Daten konvertiere. Ist die untenstehende Methode der beste Weg?

DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] });
21
naveen

Sie könnten es tatsächlich erheblich verkürzen. Sie können sich die Erweiterungsmethode Select() als Typkonverter vorstellen. Die Konvertierung könnte dann wie folgt geschrieben werden:

List<Cards> target = dt.AsEnumerable()
    .Select(row => new Cards
    {
        // assuming column 0's type is Nullable<long>
        CardID = row.Field<long?>(0).GetValueOrDefault(),
        CardName = String.IsNullOrEmpty(row.Field<string>(1))
            ? "not found"
            : row.Field<string>(1),
    }).ToList();
30
Jeff Mercado

Ich denke, alle Lösungen können verbessert werden und die Methode allgemeiner werden, wenn Sie einige Konventionen und Überlegungen verwenden. Angenommen, Sie benennen Ihre Spalten in der Datentabelle mit demselben Namen wie die Eigenschaften in Ihrem Objekt. Dann könnten Sie etwas schreiben, das sich alle Eigenschaften Ihres Objekts ansieht, und dann diese Spalte in der Datentabelle nachschlagen, um den Wert abzubilden.

Ich habe das Gegenteil getan, das heißt ... von IList zu datatable, und der Code, den ich geschrieben habe, kann unter folgender Adresse eingesehen werden: http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension /

Es sollte nicht so schwer sein, in die andere Richtung zu gehen, und es sollte auch nicht so schwer sein, die Funktionen zu überladen, damit Sie Informationen darüber bereitstellen können, welche Eigenschaften Sie ein- oder ausschließen möchten.

EDIT: Der Code, mit dem es funktioniert, lautet also:

public static class DataTableExtensions
{
    private static Dictionary<Type,IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
    public static IList<PropertyInfo> GetPropertiesForType<T>()
    {
        var type = typeof(T);
        if(!typeDictionary.ContainsKey(typeof(T)))
        {
            typeDictionary.Add(type, type.GetProperties().ToList());
        }
        return typeDictionary[type];
    }

    public static IList<T> ToList<T>(this DataTable table) where T : new()
    {
        IList<PropertyInfo> properties = GetPropertiesForType<T>();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
        {
            var item = CreateItemFromRow<T>((DataRow)row, properties);
            result.Add(item);
        }

        return result;
    }

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
    {
        T item = new T();
        foreach (var property in properties)
        {
            property.SetValue(item, row[property.Name], null);
        }
        return item;
    }

}

Wenn Sie eine Datentabelle haben, können Sie einfach yourTable.ToList<YourType>() schreiben, und die Liste wird für Sie erstellt. Wenn Sie einen komplexeren Typ mit verschachtelten Objekten haben, müssen Sie den Code aktualisieren. Ein Vorschlag ist, einfach die ToList -Methode zu überladen, um einen params string[] excludeProperties zu akzeptieren, der alle Ihre Eigenschaften enthält, die nicht zugeordnet werden sollten. Natürlich können Sie in der foreach -Schleife der CreateItemForRow -Methode eine Nullprüfung hinzufügen.

UPDATE: Statisches Wörterbuch hinzugefügt, um das Ergebnis der Reflektionsoperation zu speichern und etwas schneller zu machen. Ich habe den Code nicht kompiliert, aber es sollte funktionieren :).

11
Tomas Jansson

Die .ToList () ist am falschen Ort, und wenn einige Felder Null sein können, müssen Sie damit umgehen, da sie nicht in Int64 konvertiert werden, wenn sie Null sind

DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().Select(
  x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList();
6
Jamiec

Nur eine kleine Vereinfachung. Ich benutze ItemArray nicht:

List<Person> list = tbl.AsEnumerable().Select(x => new Person
                    {
                        Id = (Int32) (x["Id"]),
                        Name = (string) (x["Name"] ?? ""),
                        LastName = (string) (x["LastName"] ?? "")
                    }).ToList();
6
FrenkyB

nun, es ist die einzeilige Lösung

es hängt davon ab, ob Sie wissen die Daten in der Datenbank alle gültig sind und nichts enthalten, was die oben genannten Daten verletzt

zB ein nullwertbares Feld, wenn Sie es nicht erwarten - vielleicht aufgrund eines Links-Joins in eh-SQL, der die Daten generiert.

Wenn Sie also die Daten vorab validiert haben, dann ja - ich wollte Linq vorschlagen -, aber Sie haben es geschafft.

Wenn Sie etwas Validierung benötigen, sollten Sie jedoch wahrscheinlich nur die Datenzeilen durchlaufen, Ihr Objekt wie oben beschrieben generieren und der Sammlung hinzufügen. Auf diese Weise können Sie Fehler in einer Zeile behandeln und den Rest trotzdem verarbeiten.

So sehe ich es trotzdem

(verdammt, ich kam herab, um etwas abzustimmen, also war mein Vertreter 1024)

2
John Nicholas

Komme spät, aber das kann nützlich sein. Kann aufgerufen werden mit: 

table.Map (); oder Aufruf mit einer Funktion, um die Werte zu filtern.

Sie können sogar den Zuordnungsnamen zwischen der type-Eigenschaft und dem DataColumn-Header ändern, indem Sie die Attribute für die Eigenschaft festlegen.

[AttributeUsage(AttributeTargets.Property)]
    public class SimppleMapperAttribute: Attribute
    {
        public string HeaderName { get; set; }
    }


     public static class SimpleMapper
{
    #region properties
    public static bool UseDeferredExecution { get; set; } = true;
    #endregion

#region public_interface  


    public static IEnumerable<T> MapWhere<T>(this DataTable table, Func<T, bool> sortExpression) where T:new()
    {
        var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties())).Where((t)=>sortExpression(t));
        return UseDeferredExecution ? result : result.ToArray();
    }
    public static IEnumerable<T> Map<T>(this DataTable table) where T : new()
    {
        var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties()));
        return UseDeferredExecution ? result : result.ToArray();
    }
    #endregion

#region implementation_details
    private static T ConvertRow<T>(DataRow row, DataColumnCollection columns, System.Reflection.PropertyInfo[] p_info) where T : new()
    {
        var instance = new T();
        foreach (var info in p_info)
        {
            if (columns.Contains(GetMappingName(info))) SetProperty(row, instance, info);             
        }
        return instance;
    }

    private static void SetProperty<T>(DataRow row, T instance, System.Reflection.PropertyInfo info) where T : new()
    {
        string mp_name = GetMappingName(info);
        object value = row[mp_name];
        info.SetValue(instance, value);
    }

    private static string GetMappingName(System.Reflection.PropertyInfo info)
    {
        SimppleMapperAttribute attribute = info.GetCustomAttributes(typeof(SimppleMapperAttribute),true).Select((o) => o as SimppleMapperAttribute).FirstOrDefault();
        return attribute == null ? info.Name : attribute.HeaderName;
    }
#endregion
}
0
falopsy

Hier ist eine einfache Möglichkeit, eine generische Liste in c # mit Where-Bedingung zu konvertieren

List<Filter> filter = ds.Tables[0].AsEnumerable()
                        .Where(x => x.Field<int>("FilterID") == 5)
                        .Select(row => new Filter
                        {
                            FilterID = row.Field<int>("FilterID"),
                            FilterName = row.Field<string>("FilterName")
                        }).ToList();

Definieren Sie zuerst Eigenschaften und verwenden Sie sie als 

public class Filter
{
    public int FilterID { get; set; }
    public string FilterName { get; set; }
}

Paket setzen:

using System.Linq;
using System.Collections.Generic;
0

Sie können die Datentabelle mit einer generischen Klasse wie folgt der Modellklasse zuordnen.

generische Klasse

 public static class DataTableMappingtoModel
    {
        /// <summary>
        /// Maps Data Table values to coresponded model propertise
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> MappingToEntity<T>(this DataTable dt) 
        {
            try
            {
                var lst = new List<T>();
                var tClass = typeof (T);
                PropertyInfo[] proInModel = tClass.GetProperties();
                List<DataColumn> proInDataColumns = dt.Columns.Cast<DataColumn>().ToList();
                T cn;
                foreach (DataRow item in dt.Rows)
                {
                    cn = (T) Activator.CreateInstance(tClass);
                    foreach (var pc in proInModel)
                    {


                            var d = proInDataColumns.Find(c => string.Equals(c.ColumnName.ToLower().Trim(), pc.Name.ToLower().Trim(), StringComparison.CurrentCultureIgnoreCase));
                            if (d != null)
                                pc.SetValue(cn, item[pc.Name], null);


                    }
                    lst.Add(cn);
                }
                return lst;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
    }

Modellklasse

public class Item
{
    public string ItemCode { get; set; }
    public string Cost { get; set; }
    public override string ToString()
    {
        return "ItemCode : " + ItemCode + ", Cost : " + Cost;
    }
}

DataTable erstellen  

public DataTable getTable()
{
    DataTable dt = new DataTable();
    dt.Columns.Add(new DataColumn("ItemCode", typeof(string)));
    dt.Columns.Add(new DataColumn("Cost", typeof(string)));
    DataRow dr;
    for (int i = 0; i < 10; i++)
    {
        dr = dt.NewRow();
        dr[0] = "ItemCode" + (i + 1);
        dr[1] = "Cost" + (i + 1);
        dt.Rows.Add(dr);
    }
    return dt;
}

Jetzt können wir diese DataTable wie folgt in eine Liste konvertieren:

DataTable dt = getTable();
List<Item> lst = dt.ToCollection<Item>();
foreach (Item cn in lst)
{
    Response.Write(cn.ToString() + "<BR/>");
}

Hoffnung wird dir helfen

0
dush88c

Ich habe auf der Logik von Tomas Jansson aufgebaut, um ein "Ignorieren" -Attribut aufzunehmen. Dadurch kann ich der Klasse, die geladen wird, andere Attribute hinzufügen, ohne das Laden von DataTable-To-Class selbst zu beschädigen.

Alternativ habe ich auch überlegt, einen separaten Parameter hinzuzufügen, der den tatsächlichen Spaltennamen enthält, aus dem in der DataTable gelesen werden soll. In diesem Fall würden Sie anstelle von "Zeile [Eigenschaft.Name]" die Zeile [Attribut.Name] "oder etwas Ähnliches für diese bestimmte Eigenschaft verwenden.

public static class DataTableExtensions
{
    [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
    public sealed class IgnoreAttribute : Attribute { public IgnoreAttribute() { } }

    private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();

    public static IList<PropertyInfo> GetPropertiesForType<T>()
    {
        var type = typeof(T);

        if (!typeDictionary.ContainsKey(typeof(T)))
            typeDictionary.Add(type, type.GetProperties().ToList());

        return typeDictionary[type];
    }

    public static IList<T> ToList<T>(this DataTable table) where T : new()
    {
        IList<PropertyInfo> properties = GetPropertiesForType<T>();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
            result.Add(CreateItemFromRow<T>((DataRow)row, properties));

        return result;
    }

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
    {
        T item = new T();

        foreach (var property in properties)
        {
            // Only load those attributes NOT tagged with the Ignore Attribute
            var atr = property.GetCustomAttribute(typeof(IgnoreAttribute));
            if (atr == null)
                property.SetValue(item, row[property.Name], null);
        }

        return item;
    }
}
0
Javier Rapoport