免责声明:我知道它在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<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] });
答案 0 :(得分:33)
你实际上可以大大缩短它。您可以将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();
答案 1 :(得分:10)
我认为如果使用一些约定和反射,可以改进所有解决方案并使方法更通用。假设您在数据表中将列命名为与对象中的属性相同的名称,然后您可以编写一些内容来查看对象的所有属性,然后在数据表中查找该列以映射该值。
我做了相反的事情,那就是......从IList到datatable,我写的代码可以在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;
}
}
如果你有一个DataTable,你可以写yourTable.ToList<YourType>()
,它会为你创建列表。如果您有更复杂的类型与嵌套对象,您需要更新代码。一个建议是重载ToList
方法以接受包含所有不应映射的属性的params string[] excludeProperties
。当然,您可以在foreach
方法的CreateItemForRow
循环中添加一些空值检查。
UPDATE:添加静态字典以存储反射操作的结果,使其更快一点。我没有编译代码,但它应该工作:)。
答案 2 :(得分:6)
.ToList()位置错误,如果某些字段可能为null,则必须处理这些字段,因为如果它们为空则不会转换为Int64
DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().Select(
x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList();
答案 3 :(得分:6)
稍微简化一下。我不使用ItemArray:
List<Person> list = tbl.AsEnumerable().Select(x => new Person
{
Id = (Int32) (x["Id"]),
Name = (string) (x["Name"] ?? ""),
LastName = (string) (x["LastName"] ?? "")
}).ToList();
答案 4 :(得分:2)
以及其一线解决方案
这取决于你知道数据库中的数据是否全部有效,并且不包含任何会破坏上述内容的内容
例如,当你不期望它时可以为空的字段 - 可能是由于左边的连接数据生成了数据。
所以,如果你之前已经验证了数据,那么 - 我很想建议一些linq - 但你已经失败了。
如果你需要一些验证,你可能只需循环遍历数据行,如上所述生成你的对象并将其添加到集合中...这也将允许你处理一行中的错误并仍然处理其余的。 / p>
无论如何,这就是我看待它的方式
(该死的我接下来投了一些东西,所以我的代表是1024)
答案 5 :(得分:0)
您可以使用如下所示的Generic类将Data Table映射到模型类。
通用课程
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;
}
}
创建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<Item> lst = dt.ToCollection<Item>();
foreach (Item cn in lst)
{
Response.Write(cn.ToString() + "<BR/>");
}
希望能帮到你
答案 6 :(得分:0)
我建立在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<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;
}
}
答案 7 :(得分:0)
迟到但这很有用。可以使用:
调用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<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
}
答案 8 :(得分:0)
这是一种使用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;
答案 9 :(得分:0)
这是将 DataTable 转换为泛型类型列表的最快无循环解决方案。
public static List<T> ConvertDataTable<T>(DataTable SourceData, Func<DataRow, T> RowConverter)
{
List<T> list = new List<T>();
if (SourceData == null || SourceData.Rows.Count < 1)
return list;
IEnumerable<T> enumerable = SourceData.AsEnumerable().Select(RowConverter);
if (enumerable == null)
return list;
return new List<T>(enumerable);
}
这是这个函数的实现。
public static List<T> ExecuteListOfObject<T>(DataTable SourceData)
{
return ConvertDataTable(SourceData, ConvertRecord<T>);
}
public static T ConvertRecord<T>(DataRow drData)
{
if (drData == null || drData[0] == DBNull.Value)
return default(T);
return (T)drData[0];
}