[英]In converting List<T> to DataTable add select properties from class
I am trying to convert a List to a DataTable.我正在尝试将列表转换为数据表。 I have the basic outline done, but am stuck on one part.
我已经完成了基本大纲,但被困在一个部分。 I am trying to only get a few of the properties form the class.
我试图只从 class 中获取一些属性。
public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
table.Columns.Add("type", typeof(string));
table.Columns.Add("id", typeof(Int32));
table.Columns.Add("name", typeof(string));
table.Columns.Add("city", typeof(string));
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
}
table.Rows.Add(row);
}
return table;
}
The problem is when it gets to the second property it throws an error saying column 'unincludedCol' does not belong to table.问题是当它到达第二个属性时,它会抛出一个错误,指出列“unincludedCol”不属于表。
How do I get around this?我该如何解决这个问题?
If you need just type, id, name, and city , you can add them to a list of string:如果您只需要type、id、name 和 city ,您可以将它们添加到字符串列表中:
List<string> columnNames = new List<string>
{
"type","id","name","city"
};
Or extract them from dataTable
like:或者从
dataTable
中提取它们,例如:
List<string> columnNames = new List<string>();
foreach(DataColumn column in dataTable.Columns)
{
columnNames.Add(column.ColumnName);
}
And in the loop check if columnNames
contains prop.Name
, like:并在循环中检查
columnNames
是否包含prop.Name
,例如:
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
{
if(!columnNames.Contains(prop.Name))
continue;
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
}
table.Rows.Add(row);
}
Note: instead if(.columnNames.Contains(prop.Name))
, you can use .Any()
and Equals
methods to ignore case:注意:代替
if(.columnNames.Contains(prop.Name))
,您可以使用 .Any .Any()
和Equals
方法来忽略大小写:
if (!columnNames.Any(c => c.Equals(prop.Name, StringComparison.OrdinalIgnoreCase)))
continue;
I hope you find this helpful.我希望你觉得这有帮助。
I believe using a an attribute is attribute will be more generic.我相信使用一个属性是属性会更通用。 Also Lets you assign property name aliases.
还允许您分配属性名称别名。
Attribute属性
[AttributeUsage(AttributeTargets.Property)]
public class UseAsTableColumn : Attribute
{
public string Alias { get; private set; }
public UseAsTableColumn(string columnAlias = null)
{
this.Alias = columnAlias;
}
}
A helper - Altered your method (Using Property name as the Column Name)助手 - 更改了您的方法(使用属性名称作为列名称)
public static class Helper
{
public static string GetDisplayName(PropertyInfo pi)
{
var uatAttrib = pi.GetCustomAttribute(typeof(UseAsTableColumn), true) as UseAsTableColumn;
return uatAttrib.Alias == null ? pi.Name : uatAttrib.Alias;
}
public static DataTable ToDataTable<T>(this IList<T> data)
{
var mappableAttrirbs = typeof(T).GetProperties().Where(x => x.GetCustomAttributes(typeof(UseAsTableColumn), true).FirstOrDefault() != null);
DataTable table = new DataTable();
foreach (var property in mappableAttrirbs)
{
table.Columns.Add(GetDisplayName(property), property.PropertyType);
}
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (var property in mappableAttrirbs)
{
row[GetDisplayName(property)] = property.GetValue(item) ?? DBNull.Value;
}
table.Rows.Add(row);
}
return table;
}
}
Data Model (Mark Properties you want to serialize)数据 Model(标记要序列化的属性)
public class DataModel
{
[UseAsTableColumn]
public string Type { get; set; }
[UseAsTableColumn]
public int Id { get; set; }
[UseAsTableColumn("name")]
public string Name { get; set; }
[UseAsTableColumn]
public string City { get; set; }
public string _InternalValue { get; private set; }
}
Runner赛跑者
public class Program
{
static void Main(string[] args)
{
List<DataModel> data = new List<DataModel>()
{
new DataModel() { Type="Magic", Id= 1, Name = "foo", City = "bar" },
new DataModel() { Type="Magic", Id= 2, Name = "foo1", City = "bar1" }
};
Helper.ToDataTable(data);
Console.ReadKey();
}
}
May I suggest an additional fine point to an already-good accepted answer?我可以建议一个已经很好接受的答案的额外要点吗?
In your generic method, you seem to be relying on the existence of the properties 'type', 'id', 'name' and 'city'.在您的通用方法中,您似乎依赖于属性“type”、“id”、“name”和“city”的存在。 Such things should not be left to chance.
这样的事情不应该放任不管。
To guarantee that the class <T> will have those four properties, consider constraining T in your ToDataTable method like this:为保证class <T> 将具有这四个属性,请考虑在 ToDataTable 方法中约束 T ,如下所示:
public static DataTable ToDataTable<T>(this IList<T> data) where T : IMyConstraint
{}
... where IMyConstraint is declared as... ...其中IMyConstraint被声明为...
interface IMyConstraint
{
string type { get; }
int id { get; }
string name { get; }
string city { get; }
}
This makes your generic method safe for any class <T> that implements IMyConstraint:这使您的通用方法对于任何实现 IMyConstraint 的 class <T> 都是安全的:
class SomeClass : IMyConstraint
If (as your code implies) the four properties you want in your DataTable are known, you can still iterate the names:如果(正如您的代码所暗示的那样)您想要在 DataTable 中使用的四个属性是已知的,您仍然可以迭代名称:
private static DataTable ToDataTable<T>(IEnumerable<T> data) where T : IMyConstraint
{
DataTable table = new DataTable();
Type type = typeof(T);
// "If" you already know the names of the properties you want...
string[] names = new string[] { "type", "id", "name", "city" };
foreach (var name in names) // Add columns
{
table.Columns.Add(name, type.GetProperty(name).PropertyType);
}
foreach (var item in data) // Add rows
{
object[] values =
names
.Select(name => type.GetProperty(name).GetValue(item))
.ToArray();
table.Rows.Add(values);
}
return table;
}
... or possibly cleaner-still, just iterate the IMyConstraints interface instead: ...或者可能更清洁,只需迭代 IMyConstraints 接口:
private static DataTable ToDataTableAlt<T>(IEnumerable<T> data) where T : IMyConstraint
{
DataTable table = new DataTable();
PropertyInfo[] propertyInfos = typeof(IMyConstraint).GetProperties();
foreach (var propertyInfo in propertyInfos) // Add columns
{
table.Columns.Add(propertyInfo.Name, propertyInfo.PropertyType);
}
foreach (var item in data) // Add rows
{
object[] values =
propertyInfos
.Select(propertyInfo => propertyInfo.GetValue(item))
.ToArray();
table.Rows.Add(values);
}
return table;
}
Clone or Download this working example from GitHub.从 GitHub克隆或下载此工作示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.