代码之家  ›  专栏  ›  技术社区  ›  codeandcloud

在C中将数据表转换为泛型列表#

  •  23
  • codeandcloud  · 技术社区  · 14 年前

    免责声明:我知道它被要求在这么多地方。

    编码语言:C#3.5

    我有一个名为cardsTable的数据表,它从数据库中提取数据,我有一个只有一些属性的类卡(没有构造函数)

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


    我的数据中会有空字段,因此转换数据时方法不会出错。下面的方法是最好的方法吗?

    DataTable dt = GetDataFromDB();
    List<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] });
    
    9 回复  |  直到 7 年前
        1
  •  34
  •   Jeff Mercado    13 年前

    实际上你可以把它大大缩短。你可以想到 Select()

    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();
    
        2
  •  12
  •   Sevin7 erikkallen    10 年前

    我认为如果使用一些约定和反射,所有的解决方案都可以改进,使方法更通用。假设您将数据表中的列命名为与对象中的属性相同的名称,然后您可以编写查看对象的所有属性的内容,然后在数据表中查找该列以映射值。

    http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/

    往另一个方向走不应该那么难,重载函数应该那么难,这样您就可以提供要包含或排除哪些属性的信息。

    编辑:

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

    如果你有一个数据表,你可以写 yourTable.ToList<YourType>() 它将为您创建列表。如果有更复杂的嵌套对象类型,则需要更新代码。一个建议是让 ToList params string[] excludeProperties 包含所有不应映射的属性。当然,您可以在 foreach 循环 CreateItemForRow

    更新: 添加了静态字典来存储反射操作的结果,以使其更快一些。我还没有编译代码,但它应该可以工作:)。

        3
  •  8
  •   FrenkyB    11 年前

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

    .ToList()位于错误的位置,如果某些字段可以为空,则必须处理这些字段,因为如果它们为空,则它们不会转换为Int64

    DataTable dt = GetDataFromDB();
    List<Cards> target = dt.AsEnumerable().Select(
      x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList();
    
        5
  •  2
  •   John Nicholas    14 年前

    好吧,这是唯一的解决方案

    这取决于你是否 数据库中的数据都是有效的,不包含任何会破坏上述内容的内容

    当您不希望它是空字段时,可能是因为生成数据的左连接int eh sql。

    如果您需要一些验证,但是您可能只需循环遍历数据行,生成如上所述的对象并将其添加到集合中。。。这也将允许您在一行中处理错误,并仍然处理其余的错误。

    我就是这么看的

        6
  •  1
  •   codeandcloud    9 年前

    可以使用下面这样的泛型类将数据表映射到模型类。

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

    模型类

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

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

    现在我们可以将此数据表转换为如下列表:

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

    希望对你有帮助

        7
  •  0
  •   Javier Rapoport    9 年前

    另外,我还考虑添加一个单独的参数,该参数保存要从DataTable中读取的实际列名。在这种情况下,不要使用“row[property.Name]”,而是使用row[attribute.Name]或类似的内容来表示特定的属性。

    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;
        }
    }
    
        8
  •  0
  •   falopsy    8 年前

    table.Map(); 或者使用Func调用以筛选值。

    [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
    }
    
        9
  •  0
  •   Manjunath Bilwar    6 年前

    下面是一个简单的方法,可以用Where条件在c中转换为泛型列表

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

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

    放置包:

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