【问题标题】:In converting List<T> to DataTable add select properties from class在将 List<T> 转换为 DataTable 时,从类中添加选择属性
【发布时间】:2020-06-07 03:57:12
【问题描述】:

我正在尝试将列表转换为数据表。我已经完成了基本大纲,但被困在一个部分。我试图只从类中获取一些属性。

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

问题是当它到达第二个属性时,它会抛出一个错误,指出列 'unincludedCol' 不属于表。

我该如何解决这个问题?

【问题讨论】:

  • 在您的内部foreach 中添加一个if 语句,仅当属性名称在{"type", "id", "name", "city"} 中时才起作用

标签: c#


【解决方案1】:

如果您只需要类型、id、姓名和城市,您可以将它们添加到字符串列表中:

List<string> columnNames = new List<string>
{
  "type","id","name","city"
};

或从dataTable 中提取它们,例如:

List<string> columnNames = new List<string>();

foreach(DataColumn column in dataTable.Columns)
{
    columnNames.Add(column.ColumnName);
}

并在循环中检查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);
}

注意:代替if(!columnNames.Contains(prop.Name)),您可以使用.Any()Equals 方法来忽略大小写:

if (!columnNames.Any(c => c.Equals(prop.Name, StringComparison.OrdinalIgnoreCase)))
    continue;

希望对您有所帮助。

【讨论】:

  • 如果你能从数据表中提取列的名称会更干净。这样,名称集只指定一次
【解决方案2】:

我相信使用属性是属性会更通用。 还允许您分配属性名称别名。

属性

[AttributeUsage(AttributeTargets.Property)]
public class UseAsTableColumn : Attribute
{
    public string Alias { get; private set; }
    public UseAsTableColumn(string columnAlias = null)
    {
        this.Alias = columnAlias;
    }
}

一个助手 - 改变了你的方法(使用属性名称作为列名)

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

数据模型(标记要序列化的属性)

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

跑步者

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

【讨论】:

    【解决方案3】:

    我可以建议一个已经很好接受的答案吗?

    在您的通用方法中,您似乎依赖于属性“type”、“id”、“name”和“city”的存在。这样的事情不应该碰运气。

    为了保证 将具有这四个属性,请考虑像这样在 ToDataTable 方法中约束 T:

    public static DataTable ToDataTable<T>(this IList<T> data) where T : IMyConstraint
    {}
    

    ... IMyConstraint 被声明为 ...

    interface IMyConstraint
    {
        string type { get; }
        int id { get; }
        string name { get; }
        string city { get; }
    }
    

    这使您的泛型方法对于任何实现 IMyConstraint 的类 都是安全的:

    class SomeClass : IMyConstraint
    


    如果(正如您的代码所暗示的那样)您想要在 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;
    }
    

    ...或者可能更简洁,只需迭代 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 这个工作示例来自 GitHub。

    【讨论】:

      猜你喜欢
      • 2011-01-17
      • 2013-10-14
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 2017-08-20
      • 1970-01-01
      相关资源
      最近更新 更多