C#Generic List foreach OutofMemoryException

时间:2012-06-12 16:32:22

标签: c# foreach out-of-memory generic-list sharpkml

我有一个程序,可以将数据库中大约200万行读入List。 每行都包含一个包含地理坐标等信息的位置。

将数据添加到List后,我使用foreach循环并获取坐标以创建kml文件。当行数很大时,循环遇到OutOfMemoryException错误(但在其他情况下工作正常)。

有关如何处理此问题的任何建议,以便程序可以处理非常大的数据集? kml库是SharpKML。

我还是C#的新手,所以请放轻松!

这是循环:

            using (SqlConnection conn = new SqlConnection(connstring))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(select, conn);

            using (cmd)
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    double lat = reader.GetDouble(1);
                    double lon = reader.GetDouble(2);
                    string country = reader.GetString(3);
                    string county = reader.GetString(4);
                    double TIV = reader.GetDouble(5);
                    double cnpshare = reader.GetDouble(6);
                    double locshare = reader.GetDouble(7);

                    //Add results to list
                    results.Add(new data(lat, lon, country, county, TIV, cnpshare, locshare));
                }
                reader.Close();
            }
            conn.Close();
        }

            int count = results.Count();
            Console.WriteLine("number of rows in results = " + count.ToString());

            //This code segment generates the kml point plot

            Document doc = new Document();
            try
            {
                foreach (data l in results)
                {
                    Point point = new Point();
                    point.Coordinate = new Vector(l.lat, l.lon);

                    Placemark placemark = new Placemark();
                    placemark.Geometry = point;
                    placemark.Name = Convert.ToString(l.tiv);

                    doc.AddFeature(placemark);

                }
            }
            catch(OutOfMemoryException e)
            {
                throw e;
            }

这是列表中使用的类

        public class data
    {
        public double lat { get; set; }
        public double lon { get; set; }
        public string country { get; set; }
        public string county { get; set; }
        public double tiv { get; set; }
        public double cnpshare { get; set; }
        public double locshare { get; set; }

        public data(double lat, double lon, string country, string county, double tiv, double cnpshare,
            double locshare)
        {
            this.lat = lat;
            this.lon = lon;
            this.country = country;
            this.county = county;
            this.tiv = tiv;
            this.cnpshare = cnpshare;
            this.locshare = locshare;
        }

    }

4 个答案:

答案 0 :(得分:5)

为什么在编写之前需要存储所有数据?不是将每一行添加到列表中,而是应该在读取时处理每一行,然后忘记它。

例如,尝试像这样滚动代码:

Document doc = new Document();
while (reader.Read())
{
    // read from db
    double lat = reader.GetDouble(1);
    double lon = reader.GetDouble(2);
    string country = reader.GetString(3);
    string county = reader.GetString(4);
    double TIV = reader.GetDouble(5);
    double cnpshare = reader.GetDouble(6);
    double locshare = reader.GetDouble(7);

    var currentData = new data(lat, lon, country, county, TIV, cnpshare, locshare));

    // write to file
    Point point = new Point();
    point.Coordinate = new Vector(currentData.lat, currentData.lon);

    Placemark placemark = new Placemark();
    placemark.Geometry = point;
    placemark.Name = Convert.ToString(currentData.tiv);

    doc.AddFeature(placemark);
}

这只有在Document合理实施的情况下才有效。

答案 1 :(得分:2)

奥利弗是对的(从我这里投票)。性能方面你可以做一些其他的事情。首先不要查询您不会使用的字段。然后在while语句(?)之前移动所有变量声明(Oliver的代码)。最后,不是等待你的sql server收集并发回所有记录,而是逐步执行步骤。例如,如果您的记录具有UID,并且获取它们的顺序是此UID,则从本地C#变量“var lastID = 0”开始,将您的select语句更改为类似(预格式化)“select top 1000 ...其中UID> lastID“并重复您的查询,直到您得不到任何内容或任何少于1000条记录的内容。

答案 2 :(得分:1)

如果使用数据库中的数据填充列表没有大的延迟,并且您没有提到使用数据填充列表时出现问题,为什么不立即创建Point和Placemark对象。代码如下。

    var doc = new Document();

    using (SqlConnection conn = new SqlConnection(connstring))
    {
        conn.Open();
        SqlCommand cmd = new SqlCommand(select, conn);

        using (cmd)
        {
            var reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                double lat = reader.GetDouble(1);
                double lon = reader.GetDouble(2);
                string country = reader.GetString(3);
                string county = reader.GetString(4);
                double TIV = reader.GetDouble(5);
                double cnpshare = reader.GetDouble(6);
                double locshare = reader.GetDouble(7);

                var point = new Point();
                point.Coordinate = new Vector(lat , lon );

                var placemark = new Placemark();
                placemark.Geometry = point;
                placemark.Name = Convert.ToString(TIV);

                doc.AddFeature(placemark);

            reader.Close();
        }
        conn.Close();
    }

如果没有充分的理由在内存中检索如此多的数据,请尝试使用一些延迟加载方法。

答案 3 :(得分:1)

@drdigit,

我会避免在循环中执行查询。一个查询应始终返回该时刻所需的数据。在这种情况下,您将有1000个返回1000行的查询。也许最好快速显示前1000行,但我不确定如果你在循环中执行1000个更快的查询而不是只执行一个查询它会更快。也许我错了....

我认为如果在这种情况下需要延迟加载,你的方法很适合。