在C#中将DataTable转换为通用列表

免责声明:我知道它在SO的很多地方被问过。
我的查询有点不同。

编码语言:C#3.5

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

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

我想将cardsTable数据插入List类型的对象。
我的数据将包含空字段,因此当我转换数据时该方法不应该出错。 以下方法是最好的方法吗?

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

你实际上可以大大缩短它。 您可以将Select()扩展方法视为类型转换器。 然后可以将转换写为:

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

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

我做了相反的事情,那就是……从IList到datatable,我写的代码可以在http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/看到

从另一个方面来说应该不难,并且应该很难使function过载,以便您可以提供要包含或排除哪些属性的信息。

编辑:所以使其工作的代码是:

 public static class DataTableExtensions { private static Dictionary> typeDictionary = new Dictionary>(); public static IList GetPropertiesForType() { var type = typeof(T); if(!typeDictionary.ContainsKey(typeof(T))) { typeDictionary.Add(type, type.GetProperties().ToList()); } return typeDictionary[type]; } public static IList ToList(this DataTable table) where T : new() { IList properties = GetPropertiesForType(); IList result = new List(); foreach (var row in table.Rows) { var item = CreateItemFromRow((DataRow)row, properties); result.Add(item); } return result; } private static T CreateItemFromRow(DataRow row, IList properties) where T : new() { T item = new T(); foreach (var property in properties) { property.SetValue(item, row[property.Name], null); } return item; } } 

如果您有一个DataTable,您只需编写yourTable.ToList() ,它就会为您创建列表。 如果您有更复杂的类型与嵌套对象,您需要更新代码。 一个建议是重载ToList方法以接受params string[] excludeProperties ,其中包含不应映射的所有属性。 当然,您可以在CreateItemForRow方法的foreach循环中添加一些空检查。

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

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

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

只是一点点简化。 我不使用ItemArray:

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

以及它的一线解决方案

这取决于您是否知道数据库中的数据是否全部有效,并且不包含任何会破坏上述内容的内容

例如,当你没有想到它时可以为空的字段 – 可能是由于左连接数据生成数据。

所以,如果你之前已经validation了数据,那么 – 我很想建议一些linq – 但是你已经失败了。

如果你需要一些validation,你应该只是循环遍历数据行,生成你上面的对象并将其添加到集合中…这也将允许你处理一行中的错误并仍然处理其余的。

无论如何,这就是我看到它的方式

(该死的我接下来投了一些东西,所以我的代表是1024)

您可以使用如下所示的Generic类将Data Table映射到模型类。

通用类

  public static class DataTableMappingtoModel { ///  /// Maps Data Table values to coresponded model propertise ///  ///  ///  ///  public static List MappingToEntity(this DataTable dt) { try { var lst = new List(); var tClass = typeof (T); PropertyInfo[] proInModel = tClass.GetProperties(); List proInDataColumns = dt.Columns.Cast().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; } } 

创建DataTable

 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转换为List,如下所示:

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

希望能帮到你

我建立在Tomas Jansson的逻辑之上,包括一个“忽略”属性。 这允许我将其他属性添加到正在加载的类中,而不会破坏DataTable-To-Class加载本身。

或者,我还考虑添加一个单独的参数,该参数保存要在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> typeDictionary = new Dictionary>(); public static IList GetPropertiesForType() { var type = typeof(T); if (!typeDictionary.ContainsKey(typeof(T))) typeDictionary.Add(type, type.GetProperties().ToList()); return typeDictionary[type]; } public static IList ToList(this DataTable table) where T : new() { IList properties = GetPropertiesForType(); IList result = new List(); foreach (var row in table.Rows) result.Add(CreateItemFromRow((DataRow)row, properties)); return result; } private static T CreateItemFromRow(DataRow row, IList 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; } } 

迟到但这可能很有用。 可以使用:

table.Map(); 或使用Func调用以过滤值。

您甚至可以通过在属性上设置属性来更改type属性和DataColumn标头之间的映射名称。

 [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 MapWhere(this DataTable table, Func sortExpression) where T:new() { var result = table.Select().Select(row => ConvertRow(row, table.Columns, typeof(T).GetProperties())).Where((t)=>sortExpression(t)); return UseDeferredExecution ? result : result.ToArray(); } public static IEnumerable Map(this DataTable table) where T : new() { var result = table.Select().Select(row => ConvertRow(row, table.Columns, typeof(T).GetProperties())); return UseDeferredExecution ? result : result.ToArray(); } #endregion #region implementation_details private static T ConvertRow(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(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 } 

这是在c#中使用Where条件转换为通用列表的简单方法

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

首先定义属性并按使用方式使用

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

放包装:

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