objects lista generica dynamically convertir convert collection c# linq generics lambda datatable

dynamically - convertir datatable a lista generica c#



Convertir DataTable a la lista genérica en C# (9)

Descargo de responsabilidad: Sé que se solicita en tantos lugares en SO.
Mi consulta es un poco diferente.

Lenguaje de codificación: C # 3.5

Tengo una DataTable llamada cardsTable que extrae datos de la base de datos y tengo una Class Cards que solo tienen algunas propiedades (no constructor)

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

Quiero insertar los datos de cardsTable en un objeto de tipo Lista.
Mis datos tendrán campos nulos, por lo que el método no debería tener errores cuando convierta los datos. ¿Es el método de abajo la mejor manera?

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


Bueno, es la solución de una línea

depende de si sabe o no que los datos en la base de datos son todos válidos y no contendrán nada que rompa lo anterior

por ejemplo, un campo anulable cuando no lo esperas, tal vez debido a una combinación izquierda int eh sql que genera los datos.

Entonces, si ha validado los datos antes, sí, estaba dispuesto a sugerir un poco de linq, pero no pudo.

Si necesita alguna validación, sin embargo, probablemente debería recorrer los datarows, generar su objeto como se indicó anteriormente y agregarlo a la colección ... esto también le permitirá manejar los errores en una fila y aún procesar el resto.

Esa es la forma en que lo veo de todos modos

(maldita sea, vine a bajar de tono algo así que mi reputación fue 1024)


Construí sobre la lógica de Tomas Jansson para incluir un atributo "Ignorar". Esto me permite agregar otros atributos a la clase que se está cargando sin interrumpir la carga de DataTable-to-Class.

Alternativamente, también consideré agregar un parámetro separado que contenga el nombre real de la columna para leer en el DataTable. En ese caso, en lugar de usar "row [property.Name]" entonces usarías row [attribute.Name] "o algo así para esa propiedad en particular.

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


Creo que todas las soluciones pueden mejorarse y hacer que el método sea más general si utiliza algunas convenciones y reflexiones. Supongamos que nombra sus columnas en el datatable con el mismo nombre que las propiedades en su objeto, luego podría escribir algo que analice todas las propiedades de su objeto y luego buscar esa columna en el datatable para asignar el valor.

Hice lo contrario, es decir, de IList a datatable, y el código que escribí se puede ver en: http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/

No debería ser tan difícil ir hacia el otro lado, y debería ser tan difícil sobrecargar las funciones para que pueda proporcionar información sobre qué propiedades desea incluir o excluir.

EDIT: Así que el código para que funcione es:

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

Si tiene una tabla de datos, puede escribir su yourTable.ToList<YourType>() y creará la lista para usted. Si tiene un tipo más complejo con objetos anidados, necesita actualizar el código. Una sugerencia es simplemente sobrecargar el método ToList para aceptar una params string[] excludeProperties que contiene todas sus propiedades que no deberían asignarse. Por supuesto, puede agregar algunas comprobaciones nulas en el bucle foreach del método CreateItemForRow .

ACTUALIZACIÓN: Se agregó un diccionario estático para almacenar el resultado de la operación de reflexión para hacerlo un poco más rápido. No he compilado el código, pero debería funcionar :).


De hecho, podrías acortarlo considerablemente. Puede pensar en el método de extensión Select() como un convertidor de tipo. La conversión podría escribirse así:

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


Esta es una forma sencilla de convertir a la lista genérica en c # con la condición Where

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

Primero defina propiedades y use según

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

Poner paquete:

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


La .ToList () está en el lugar equivocado, y si algunos campos pueden ser nulos, tendrá que lidiar con estos ya que no se convertirán a Int64 si están nulos

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


Llegar tarde pero esto puede ser útil. Puede ser llamado usando:

table.Map (); o llame con un Func para filtrar los valores.

Incluso puede cambiar el nombre de la asignación entre la propiedad type y el encabezado DataColumn configurando los atributos en la propiedad.

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


Puede asignar la tabla de datos a la clase modelo utilizando una clase genérica como la que se muestra a continuación.

Clase genérica

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

Clase de modelo

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

Crear tabla de datos

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

Ahora podemos convertir este DataTable a la lista como a continuación:

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

La esperanza te ayudara


Solo una pequeña simplificación. No uso ItemArray:

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