如何在同一数据文件上运行多个LINQ查询

时间:2012-05-22 15:47:06

标签: c# linq

我是LINQ的新手,我目前正在使用它处理csv格式的大数据集(50万条记录)。我使用StreamReader打开文件并实现IEnumerable<>接口以填充结果。您可以在下面看到阅读代码的主要部分:

IEnumerator<Person> IEnumerable<Person>.GetEnumerator()
{
    using (StreamReader streamReader = new StreamReader(filename)){
        streamReader.ReadLine();
        while (!streamReader.EndOfStream){
            string[] values = streamReader.ReadLine().Split(new char[] { ',' });
            Person p = new Person();
            p.Name = values[0];
            p.Age = Convert.ToInt16(values[1]);
            p.Score = Convert.ToDouble(values[2]);
            p.PlotArea = Convert.ToInt16(values[3]);
            p.ForecastConsumption = Convert.ToDouble(values[4]);
            p.Postcode = values[5];
            p.PropertyType = values[6];
            p.Bedrooms = Convert.ToInt16(values[7]);
            p.Occupancy = Convert.ToInt16(values[8]);

            yield return p;
        }
    }
}

这是一个典型的查询:

var query = from person in reader
            where person.Score > 36.55 && person.Bedrooms < 3
            select person;

我的问题是,每次我想运行查询时StreamReader都必须打开文件。有什么办法可以打开文件一次并运行多个查询吗?

仅供参考我对LINQ印象非常深刻,运行上面的查询需要1.2秒。只是我会为数据集运行很多规则。

3 个答案:

答案 0 :(得分:2)

  

我的问题是,每次我想运行查询时,StreamReader都必须打开文件。有什么办法可以打开文件一次并运行多个查询吗?

最简单的方法是将整个文件加载到列表中,例如

var list = reader.ToList();

// Now run multiple queries over list

显然这需要相当多的内存,但这将是最简单的方式。如果你想一起加入多个查询,你必须完成 你想要做的事情 - LINQ中的组合模型主要是在链接查询操作方面一起而不是从同一来源构建多个查询。

如果不这样做,如果“一次通过多个查询”的复杂性和“将整个文件加载到内存中”的复杂性都不适合你,那么你很可能会多次加载它。

可能内存效率更高的一个中间选项是将所有读入内存(因此您只执行一次磁盘活动)但是< em>多次解析这些行。就IO而言,这将更有效率,但在CPU方面更差。

答案 1 :(得分:1)

您的情况将是以下之间的绩效权衡:

  1. 将整个文件读入内存,并运行您需要的任何查询
  2. 简单地遍历文件多次。
  3. 如果是后者,请尝试使用File.ReadLines,它在文件IO上提供了一个漂亮的IEnumerable接口:

    public Person ReadPerson(string[] personLine)
    {
        Person p = new Person();
        p.Name = personLine[0];
        p.Age = Convert.ToInt16(personLine[1]);
        p.Score = Convert.ToDouble(personLine[2]);
        p.PlotArea = Convert.ToInt16(personLine[3]);
        p.ForecastConsumption = Convert.ToDouble(personLine[4]);
        p.Postcode = personLine[5];
        p.PropertyType = personLine[6];
        p.Bedrooms = Convert.ToInt16(personLine[7]);
        p.Occupancy = Convert.ToInt16(personLine[8]);
    }
    

    用法:

    var file = File.ReadLines("/filepath/")
        .Select(line => ReadPerson(line.Split(',')));
    
    var query = from person in file
        where person.Score > 36.55 && person.Bedrooms < 3
        select person;
    

答案 2 :(得分:1)

这应该有效:

  return from line in File.ReadAllLines(filename)
                     let values = line.Split(new char[] { ',' })
                     select new Person{
                Name = values[0];
                Age = Convert.ToInt16(values[1]);
                Score = Convert.ToDouble(values[2]);
                PlotArea = Convert.ToInt16(values[3]);
                ForecastConsumption = Convert.ToDouble(values[4]);
                Postcode = values[5];
                PropertyType = values[6];
                Bedrooms = Convert.ToInt16(values[7]);
                Occupancy = Convert.ToInt16(values[8]);
            };