Как уменьшить объем памяти с помощью больших наборов данных в EF5?

Я пытаюсь вытащить большой набор данных (1,4 миллиона записей) из SQL Server и дампа в файл в приложении WinForms. Я попытался сделать это с помощью подкачки, чтобы не держать слишком много в памяти сразу, но процесс продолжает расти, поскольку он работает. Около 25% через, это занимало 600,000 K. я делаю пейджинг неправильно? Могу ли я получить некоторые предложения о том, как сохранить использование памяти от роста так много?

var query = (from organizations in ctxObj.Organizations
                 where organizations.org_type_cd == 1
                 orderby organizations.org_ID
                 select organizations);
int recordCount = query.Count();
int skipTo = 0;
int take = 1000;
if (recordCount > 0)
{
    while (skipTo < recordCount)
    {
        if (skipTo + take > recordCount) 
            take = recordCount - skipTo;

        foreach (Organization o in query.Skip(skipTo).Take(take))
        {
            writeRecord(o);
        }
        skipTo += take;
    }
}

4 ответов


избавиться от подкачки и использовать AsNoTracking.

Тестовый Код

 static void Main(string[] args)
        {
            var sw = new Stopwatch();
            sw.Start();
            using (var context = new MyEntities())
            {
                var query = (from organizations in context.LargeSampleTable.AsNoTracking()
                             where organizations.ErrorID != null
                             orderby organizations.ErrorID
                             select organizations);//large sample table, 146994 rows

                foreach (MyObject o in query)
                {
                    writeRecord(o);
                }

            }
            sw.Stop();

            Console.WriteLine("Completed after: {0}", sw.Elapsed);
            Console.ReadLine();
        }

        private static void writeRecord(ApplicationErrorLog o)
        {
            ;
        }

Результат Теста:

потребление памяти уменьшено: 96%
Время выполнения сокращено:50%

толкование

AsNoTracking предоставляет преимущества для использования памяти по очевидным причинам, нам не нужно поддерживать ссылки на объекты, когда мы загружаем их в память. Объекты практически сразу становятся доступными. Объединить ленивый оценке и AsNoTracking, и нет необходимости в пейджинге, и уничтожение контекста может быть отложено.

хотя это один тест, большое количество строк и исключение большинства внешних факторов делают это хорошим представлением для общего случая.


контекст объекта будет хранить объекты в памяти, пока он не будет удален. Я бы рекомендовал удалить контекст после каждой партии, чтобы предотвратить дальнейшее увеличение объема памяти.

вы также можете использовать AsNoTracking() (http://msdn.microsoft.com/en-us/library/gg679352(В=и 103).аспн), поскольку не сохраняются в базе данных.


несколько вещей.

  1. вызов Count() выполнить запрос. Затем запустить его второй раз, чтобы получить результаты. Тебе не нужно этого делать.

  2. память, которую вы видите, связана с загрузкой объектов в память. Если вам нужно только подмножество полей, проецируйте на анонимный тип (или более простой именованный тип.) Это позволит избежать любых изменений отслеживания и накладных расходов.

используется таким образом, EF может быть хорошим строго типизированным API легкий SQL-запросов.

что-то вроде этого должно сделать трюк:

var query = from organizations in ctxObj.Organizations
             where organizations.org_type_cd == 1
             orderby organizations.org_ID
             select new { o.Id, o.Name };

foreach (var org in query)
{
    write(org.Id, org.Name);
}

почему бы вам просто не использовать стандартный System.Data.SqlClient.SqlConnection класса? Вы можете прочитать результаты командной строки по строкам, используя SqlDataReader class и запишите каждую строку в файл. У вас есть полный контроль, чтобы гарантировать, что ваш код ссылается только на одну строку записей за раз.

using (var writer = new System.IO.StreamWriter(fileName))
using (var conn = new SqlConnection(connectionString))
{
    using (var cmd = new SqlCommand())
    {
        cmd.CommandText = "SELECT * FROM Organizations WHERE org_type_cd = 1 ORDER BY org_ID";

        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int id = (int)reader["org_ID"];
                int org_type_cd = (int)reader["org_type_cd"];

                writer.WriteLine(...);
            }
        }
    }
}

Entity Framework не предназначен для решения каждой проблемы или быть вашим эксклюзивным фреймворком доступа к данным. Это означает, что вещи проще писать для простых операций CRUD. Работа с миллионами строк-это хороший вариант использования для более специализированного решения.