将数据表转换为通用列表的最快方法

时间:2009-01-13 22:08:10

标签: .net .net-2.0 datatable n-tier-architecture

我有一个返回数据表的数据层选择方法。它是从业务层方法调用的,然后应返回强类型的通用List。

我想做的是与这个问题非常相似(但不一样):
How do you convert a DataTable into a generic list?

有什么不同之处在于我希望列表包含强类型对象而不是数据行(此外,我还没有可用的linq)。

我很关心表现。然后将从表示层调用业务层方法,并且将迭代结果以显示给用户。在业务层添加额外的迭代似乎非常浪费,只能立即再次进行演示,所以我希望尽可能快。

这是一项常见的任务,所以我真的在寻找一个可以反复重复的好模式。

6 个答案:

答案 0 :(得分:9)

上面的示例问题在于它非常慢。我有一个大约有400行的DataTable,这个转换需要5到6秒!

这似乎是一项非常常见的任务,所以我很惊讶没有在这里看到有更高效的解决方案。

*更新!! * 只是为了踢,我想我会尝试使用LINQ进行转换,而不是迭代DataTable并添加到我的List。以下是我的所作所为:

   List<MyObject> returnList = new List<MyObject>();

   MyDataTable dtMyData = new MyTableAdapter().GetMyData();

   returnLists = (from l in dtMyData
                 select new MyObject
                 {
                    Active = l.IsActive,
                    Email = l.Email,
                    //...
                    //About 40 more properties
                    //...
                    ZipCode = l.Zipcode
                  }).ToList();

第一种方法(遍历每一行)花了5.3秒,使用LINQ的方法耗时1.8秒!

答案 1 :(得分:6)

您是否提前知道DataTable和打字对象的结构?您可以使用委托来执行映射。如果你不这样做(即你所知道的只有Type和属性),就有办法加速动态成员访问(例如HyperDescriptor)。

无论哪种方式,考虑一个迭代器块;这样你不必第二次缓冲对象;当然,如果你只处理小行数,这不是问题。

你能澄清其中的任何一点吗?我可以添加更多细节...

最简单的,有什么问题:

DataTable table = new DataTable {
    Columns = {
        {"Foo", typeof(int)},
        {"Bar", typeof(string)}
     }
};
for (int i = 0; i < 5000; i++) {
    table.Rows.Add(i, "Row " + i);
}

List<MyType> data = new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows) {
    data.Add(new MyType((int)row[0], (string)row[1]));
}

(上述问题可能引导正确的方法......)

答案 2 :(得分:0)

您需要遍历数据行并在某个时候将它们转换为您的对象,但您可以使用构造函数编写自定义集合,该构造函数接收数据行集合并在请求时将每个项目转换为您的对象类型一下子。

假设您定义了实现IList<T>的自定义集合。然后它可以有两个内部字段 - 对数据行集合的引用和List<T>List<T>将初始化为数据行集合的长度,但值为空值。

现在在索引器中,您可以检查List<T>是否包含该索引处的值,如果不是,您可以使用任何有用的方法创建对象,并在返回之前将其存储在那里。

这将推迟创建对象,直到请求它们为止,您只会创建所请求的对象。

您的对象可能需要一个构造函数来获取DataRow,或者您需要某种工厂来创建它们,但这是另一个主题。

答案 3 :(得分:0)

只需在一个简单的控制台应用程序中为Mark的答案提供更多用户友好性:

class Program
{
    static void Main(string[] args)
    {
        //define a DataTable obj
        DataTable table = new DataTable
        {
            Columns = {
            {"Foo", typeof(int)},
            {"Bar", typeof(string)}
         }
        };
        //populate it the DataTable 
        for (int i = 0; i < 3; i++)
        {
            table.Rows.Add(i, "Row " + i);
        }

        List<MyType> listWithTypedObjects= new List<MyType>(table.Rows.Count);
        foreach (DataRow row in table.Rows)
        {
            listWithTypedObjects.Add(new MyType((int)row[0], (string)row[1]));
        }

        Console.WriteLine(" PRINTING THE POPULATED LIST ");
        foreach (MyType objMyType in listWithTypedObjects)
        {
            Console.Write(" I have object of the type " + objMyType.ToString() + " => " );
            Console.Write(" with Prop1OfTypeInt " + objMyType.Prop1OfTypeInt.ToString() + " , ");
            Console.WriteLine(" with Prop1OfTypeInt " + objMyType.Prop2OfTypeString.ToString() + "  "); 
        }

        Console.WriteLine(" \n \n \n HIT A KEY TO EXIT THE PROGRAM ");
        Console.ReadKey();
    }
}

class MyType {

    public int Prop1OfTypeInt { get; set; }
    public string Prop2OfTypeString { get; set; } 

    /// <summary>
    /// Note the order of the passed parameters is important !!!
    /// </summary>
    public MyType( int prop1OfTypeInt , string prop2OfTypeString)
    {
        this.Prop1OfTypeInt = prop1OfTypeInt;
        this.Prop2OfTypeString = prop2OfTypeString; 

    }
}

答案 4 :(得分:0)

我知道这个答案很晚,但是我花了一个小时以上的时间来准备这段代码,因为我首先需要这个,然后我想找一个解决这个问题的人可能会用到这个。.

为了使我的答案正确运行,您的列表属性需要使用与数据库表列(字段)或数据表列名称相同的名称。

解决方案:

public List<dynamic> GetListFromDT(Type className, DataTable dataTable)
        {
            List<dynamic> list = new List<dynamic>();
            foreach (DataRow row in dataTable.Rows)
            {
                object objClass = Activator.CreateInstance(className);
                Type type = objClass.GetType();
                foreach (DataColumn column in row.Table.Columns)
                {
                    PropertyInfo prop = type.GetProperty(column.ColumnName);
                    prop.SetValue(objClass, row[column.ColumnName], null);   
                }
                list.Add(objClass);
            }
            return list;
        }

如何使用?

假设您有一个填充了值的数据表dtPersons

DataTable dtPersons; //populate this datatable

那么可以说您有一个具有以下属性的类。

public class Persons{
    public string Name {get; set;}
    public int Age {get; set;}
}

现在将方法打包并放入模型中。 调用如下方法。

List<dynamic> dynamicListReturned = GetListFromDT(typeof(Persons), dataTable);
List<Persons> listPersons = dynamicListReturned.Cast<Persons>().ToList();

就是这样,现在您从数据表中的 listPersons 中获得了列表。

  

请记住:类属性中的名称和   数据表/数据库应该相同

答案 5 :(得分:-3)

public partial class issuereceive_manageroffice_bal
{
    public int issue_id{get;set;}
    public string process{get;set;}
    public DateTime issue_date{get;set;}
    public TimeSpan issue_time{get;set;}
    public string eg_no{get;set;}
    public string lotno{get;set;}
    public string clarity{get;set;}
    public string sieves{get;set;}
    public string shape{get;set;}
    public double issue_carat{get;set;}
    public int issue_pieces{get;set;}
    public int receive_pieces{get;set;}
    public double receive_carat{get;set;}
    public int kp_pieces{get;set;}
    public decimal kp_carat{get;set;}
    public double loss{get;set;}
    public string issue_manager{get;set;}
    public string issue_by{get;set;}
    public string receive_by{get;set;}
    public int status{get;set;}
    public DateTime receive_date{get;set;}
    public string receive_time{get;set;}
    public int factory_id{get;set;}

}


List<issuereceive_manageroffice_bal> issue_receive_list = new List<issuereceive_manageroffice_bal>();
issue_receive_list =
      (from DataRow dr in DataTable.Rows
      select new issuereceive_manageroffice_bal()
           {
               issue_id = 0,
               issue_time = TimeSpan.Parse("0"),
               receive_time = null,
               shape = null,
               process = dr["process"].ToString(),
               issue_date = Convert.ToDateTime(dr["issue_date"]),
               eg_no = dr["eg_no"].ToString(),
               lotno = dr["lotno"].ToString(),
               clarity = dr["clarity"].ToString(),
               sieves = dr["sieves"].ToString(),
               issue_carat = dr["issue_carat"].ToString() != "" ? double.Parse(dr["issue_carat"].ToString()) : 0,
               issue_pieces = dr["issue_pieces"].ToString() != "" ? int.Parse(dr["issue_pieces"].ToString()) : 0,
               receive_carat = dr["receive_carat"].ToString() != "" ? double.Parse(dr["receive_carat"].ToString()) : 0,
               kp_pieces = dr["kp_pieces"].ToString() != "" ? int.Parse(dr["kp_pieces"].ToString()) : 0,
               kp_carat = dr["kp_carat"].ToString() != "" ? decimal.Parse(dr["kp_carat"].ToString()) : 0,
               loss = dr["loss"].ToString() != "" ? double.Parse(dr["loss"].ToString()) : 0,
               issue_manager = dr["lotno"].ToString(),
               issue_by = dr["issue_by"].ToString(),
               receive_by = dr["receive_by"].ToString(),
               status = dr["status"].ToString() != "" ? int.Parse(dr["status"].ToString()) : 0,
               receive_date = Convert.ToDateTime(dr["receive_date"]),
               factory_id = dr["factory_id"].ToString() != "" ? int.Parse(dr["factory_id"].ToString()) : 0,

           }).ToList();