简体   繁体   English

C#Generic List foreach OutofMemoryException

[英]C# Generic List foreach OutofMemoryException

I have a program that reads approximately 2million rows from a database into a List. 我有一个程序,从数据库中读取大约200万行到List。 Each row is a location that contains information such as geographic co-ordinates. 每行是包含地理坐标等信息的位置。

Once data is added to the List I use a foreach loop and grab the co-ordinates to create a kml file. 将数据添加到List后,我使用foreach循环并获取坐标以创建kml文件。 The loop encounters an OutOfMemoryException error when the number of rows is large (but works perfectly otherwise). 当行数很大时,循环遇到OutOfMemoryException错误(但是否则完美地工作)。

Any suggestions on how to handle this so that the program can work with very large sets of data? 有关如何处理此问题的任何建议,以便程序可以处理非常大的数据集? The kml library is SharpKML. kml库是SharpKML。

I am still new to C# so please go easy! 我还是C#的新手,所以请放轻松!

This is the loop: 这是循环:

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

This is the class uused in the List 这是列表中使用的类

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

    }

Why do you need to store all the data before writing it? 为什么在编写之前需要存储所有数据? Rather than adding each row to a list, you should process each row as it is read, then forget about it. 不是将每一行添加到列表中,而是应该在读取时处理每一行,然后忘记它。

For instance, try rolling your code together like this: 例如,尝试将代码一起滚动如下:

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

This will only work if Document is implemented sensibly though. 这只有在Document合理实施的情况下才有效。

Oliver is right (up-vote from me). 奥利弗是对的(从我这里投票)。 Performance wise you can do some other stuff. 性能方面你可以做一些其他的事情。 First do not query for fields you're not going to use. 首先不要查询您不会使用的字段。 Then move all variable declarations (Oliver's code) before the while statement (?). 然后在while语句(?)之前移动所有变量声明(Oliver的代码)。 Finally instead of waiting your sql server to collect and send all records back, do it progressively with steps. 最后,不是等待你的sql server收集并发回所有记录,而是逐步执行步骤。 For example if your records have an UID and the order to get them is this UID then start with a local C# variable "var lastID = 0", change your select statement to something like (pre-format) "select top 1000 ... where UID > lastID" and repeat your queries until you get nothing or anything will less than 1000 records. 例如,如果您的记录具有UID,并且获取它们的顺序是此UID,则从本地C#变量“var lastID = 0”开始,将您的select语句更改为类似(预格式化)“select top 1000 ...其中UID> lastID“并重复您的查询,直到您什么都没有或任何东西少于1000条记录。

If there is no big delay in populating list with data from database and you did not mentioned problems with populating list with data, why not immediately create your Point and Placemark object. 如果使用数据库中的数据填充列表没有大的延迟,并且您没有提到使用数据填充列表时出现问题,为什么不立即创建Point和Placemark对象。 Code is below. 代码如下。

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

If there is no good reason for retrieving so many data in memory, try with some lazy loading approach. 如果没有充分的理由在内存中检索如此多的数据,请尝试使用一些延迟加载方法。

@drdigit, @drdigit,

I would avoid executing queries in loop. 我会避免在循环中执行查询。 One query should always return as much data as it is needed in that moment. 一个查询应始终返回该时刻所需的数据。 In this case you would have 1000 queries that returns 1000 rows. 在这种情况下,您将有1000个返回1000行的查询。 Maybe it is better for quickly showing first 1000 rows, but I'm not sure if it will be faster if you execute 1000 faster queries in loop instead you execute only one query.Maybe I'm wrong.... 也许最好快速显示前1000行,但我不确定如果你在循环中执行1000个更快的查询而不是只执行一个查询它会更快。也许我错了....

I think your approach is good for lazy loading if there is need for that in this situation. 我认为如果在这种情况下需要,你的方法对延迟加载很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM