Цикл по большому количеству данных в списке списка строк сбой Visual Studio C # - PullRequest
1 голос
/ 19 апреля 2019

Я реализую функциональность для веб-страницы, которая будет одновременно загружать большой объем данных (примерно 3х10 ^ 5 строк) в разные таблицы в базе данных.Пользователь сгенерирует файл Excel с этими данными и загрузит его на сервер.Я использую C # MVC для создания веб-страницы и библиотеку «ExcelDataReader» для чтения файла Excel.В начале я использовал метод «.AsDataSet» для извлечения данных, но чтение DataTable было чрезвычайно медленным и неэффективным в моем сценарии, поэтому я создал свою собственную функцию, которая читает строку в строке и сохраняет все в списке спискаСтроки.Так как я имею дело со многими таблицами, которые имеют отношение один ко многим с другими таблицами в моей базе данных, я использую словари для хранения сущностей, созданных в процессе, поэтому их получение будет легче, если они понадобятся мне позже.Теоретически, все операции в моем цикле for - это O (1), поэтому я не понимаю двух вещей:

- требуется более пяти минут, чтобы перебрать половину всех строк - когда он достигает половиныу меня только что происходит сбой в Visual Studio без сообщений.

Я использую Visual Studio 2015, и у меня статистика ПК: i5 6500 + ram 8GB ddr4 Единственная программа, которую я запускаю, кроме Visual Studio, - Microsoft edge

Таблицы в моей базе данных имеют индексы, поэтому теоретически их запрос, как будто я выполняю цикл for, не должен замедлять его.

ОБНОВЛЕНИЕ Я установил несколько секундомеров и выглядит какраздел под словом «MATRICULA» замедляет все.Выполнение этого раздела занимает 00: 00: 00.0018949 за итерацию.Любое предложение, как я могу улучшить это?

Кроме того, я отключаю диагностические инструменты Visual Studio, и он больше не падает, но все это занимает около 15 минут, и я хотел бы сделать это быстрее.

var nuevosALumnos = new List<Alumno>(cantidadFilas);
var nuevosPeriodos = new List<Periodo>();
var nuevasSecciones = new List<Seccion>(cantidadFilas);
var nuevosCursos = new List<Curso>(cantidadFilas);
var nuevasLineas = new List<Linea>();
var nuevasMatriculas = new List<Matricula>(cantidadFilas);

var periodosUsados = new Dictionary<String, Periodo>();
var alumnosUsados = new Dictionary<String, Alumno>(cantidadFilas);
var seccionesUsadas = new Dictionary<Tuple<String, String, String>, Seccion>(cantidadFilas);
var cursosUsados = new Dictionary<String, Curso>(cantidadFilas);
var lineasUsadas = new Dictionary<String, Linea>();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

for (int i = 1; i < cantidadFilas; i++)
{
    if (dataTable[i][7]  != "")
    {
        if (!alumnosUsados.ContainsKey(dataTable[i][7] ))
        {
            llaveAuxiliar = dataTable[i][7] ;
            Alumno buscarAlumno = context.Alumno.FirstOrDefault(x => x.codigo == llaveAuxiliar);
            if (buscarAlumno == null)
            {
                buscarAlumno = new Alumno();

              ```
              gathering data
              ```
                nuevosALumnos.Add(buscarAlumno);
            }
            alumnosUsados.Add(buscarAlumno.codigo, buscarAlumno);
        }
    }
    else {
        datosErroneos.Add("En la fila: " + i + " columna: H el codigo de alumno es nulo");
    }

    if (dataTable[i][1] != "")
    {
        if (!periodosUsados.ContainsKey(dataTable[i][1] ))
        {
            llaveAuxiliar = dataTable[i][1] ;
            var buscarPeriodo = context.Periodo.FirstOrDefault(x => x.codigo_periodo == llaveAuxiliar);
            if (buscarPeriodo == null)
            {
                buscarPeriodo = new Periodo();
                buscarPeriodo.codigo_periodo = dataTable[i][1] ;
                // context.Periodo.Add(buscarPeriodo);
                nuevosPeriodos.Add(buscarPeriodo);
            }
            periodosUsados.Add(dataTable[i][1] , buscarPeriodo);
        }
    } else
    {
        datosErroneos.Add("En la fila: " + i + " columna: B el codigo del ciclo es nulo");
    }

    if (dataTable[i][6]  != ConstantHelpers.TIPO_MATRICULA_EXTRANJERO)
    {
        if (dataTable[i][26]  != "")
        {
            if (!lineasUsadas.ContainsKey(dataTable[i][26] ))
            {
                llaveAuxiliar = dataTable[i][26] ;
                var buscarLinea = context.Linea.FirstOrDefault(x => x.descripcion == llaveAuxiliar);
                if (buscarLinea == null)
                {
                    buscarLinea = new Linea();
                    buscarLinea.descripcion = llaveAuxiliar;
                    //context.Linea.Add(buscarLinea);
                    nuevasLineas.Add(buscarLinea);
                }
                lineasUsadas.Add(dataTable[i][26] , buscarLinea);
            }
        }
        else
        {
            datosErroneos.Add("En la fila: " + i + " columna: AA la descripción de la linea está vacía");
        }

        if (dataTable[i][33]  != "")
        {
            if (!cursosUsados.ContainsKey(dataTable[i][33] ))
            {
                llaveAuxiliar = dataTable[i][33] ;
                var buscarCurso = context.Curso.FirstOrDefault(x => x.codigo == llaveAuxiliar);
                if (buscarCurso == null)
                {
                    buscarCurso = new Curso();

                     ```
                    gathering data
                     ```

                    buscarCurso.Linea = lineasUsadas[dataTable[i][26] ];


                    nuevosCursos.Add(buscarCurso);
                }
                cursosUsados.Add(dataTable[i][33] , buscarCurso);
            }
        }
        else
        {
            datosErroneos.Add("En la fila: " + i + " columna: Y el codigo del curso es nulo");
        }

        if (dataTable[i][30]  != "")
        {
            //codigo, periodo, curso
            if (!seccionesUsadas.ContainsKey(new Tuple<string, string, string>(dataTable[i][30], dataTable[i][1], dataTable[i][24])))
            {
                llaveAuxiliar = dataTable[i][30] ;
                llaveAuxiliar2 = dataTable[i][1] ;
                llaveAuxiliar3 = dataTable[i][24] ;

                var querySeccion = context.Database.SqlQuery<Seccion>("select a.* from Seccion a, periodo b, curso c where a.cursoId = c.id and a.periodoId = b.PeriodoId and b.codigo_periodo = '" + llaveAuxiliar2 + "' and c.codigo = '" + llaveAuxiliar3 + "'");
                Seccion buscarSeccion;
                if (querySeccion.Count() == 0)
                {

                    buscarSeccion = new Seccion();
                    buscarSeccion.codigo = dataTable[i][30] ;
                    buscarSeccion.grupo = dataTable[i][31] ;
                    buscarSeccion.Curso = cursosUsados[dataTable[i][33] ];
                    buscarSeccion.Periodo = periodosUsados[dataTable[i][1] ];
                    if (Int32.TryParse(dataTable[i][32], out auxiliar))
                    {
                        buscarSeccion.curriculo = auxiliar;
                    }
                    else
                    {
                        datosErroneos.Add("En la fila: " + i + " columna: AG no hay un número");
                    }
                    //context.Seccion.Add(buscarSeccion);
                    nuevasSecciones.Add(buscarSeccion);
                }else
                {
                    buscarSeccion = querySeccion.ElementAt(0);
                }
                seccionesUsadas.Add(new Tuple<string, string, string>(dataTable[i][30], dataTable[i][1], dataTable[i][24]), buscarSeccion);
            }
        }
        else
        {
            datosErroneos.Add("En la fila: " + i + " columna: AE el codigo de la sección es nula");
        }

        //MATRICULA
        if (dataTable[i][7]  != "" && dataTable[i][30]  != "")
        {
            auxiliar = alumnosUsados[dataTable[i][7] ].id;
            auxiliar2 = seccionesUsadas[new Tuple<string, string, string>(dataTable[i][30], dataTable[i][1], dataTable[i][24])].SeccionId;
            var objMatricula = context.Matricula.FirstOrDefault(x => x.alumnoId == auxiliar && x.seccionId == auxiliar2);

            if (objMatricula == null)
            {
                objMatricula = new Matricula();
                objMatricula.Seccion = seccionesUsadas[new Tuple<string, string, string>(dataTable[i][30], dataTable[i][1], dataTable[i][24])];
                objMatricula.Alumno = alumnosUsados[dataTable[i][7] ];
                objMatricula.sede = dataTable[i][13] ;
                if (Int32.TryParse(dataTable[i][35] , out auxiliar))
                {
                    objMatricula.cantidad_matriculas = auxiliar;
                }
                else
                {
                    datosErroneos.Add("En la fila: " + i + " columna: AJ no hay un número");
                }


                if (Int32.TryParse(dataTable[i][36] , out auxiliar))
                {
                    objMatricula.nota = auxiliar;
                    objMatricula.estado = ConstantHelpers.ESTADO_ESTUDIANDO;
                }
                else
                {
                    objMatricula.estado = ConstantHelpers.ESTADO_RETIRADO;
                }
                //context.Matricula.Add(objMatricula);
                nuevasMatriculas.Add(objMatricula);
            }

        }



    }

    cantidad++;

}

Ответы [ 2 ]

1 голос
/ 20 апреля 2019

Есть довольно много вещей, которые могут снизить производительность. Зацикливание примерно 3 миллионов строк в одном хите никогда не будет хорошей идеей. Во-первых, вам нужно зарезервировать огромное количество памяти для всех коллекций и словарей для обработки. Кроме того, существует тот факт, что DbContext открыт для всей операции, поэтому отслеживается также каждый отдельный объект, который загружен или связан с контекстом. Чем дольше контекст открыт и чем больше отслеживается сущностей, тем медленнее становятся вещи.

Далее есть другие маленькие детали, которые помогают убить производительность. Выполнение FirstOrDefault просто для проверки существования сущности является полной потерей производительности. Используйте .Any.

т.е. if (! context.Matricula.Any (x => x.alumnoId == auxiliar && x.seccionId == auxiliar2);

FirstOrDefault возвращает данные объекта или #null, Any выполняет запрос, который просто возвращает True или False, если объект существует. = Быстрее и меньше тратится памяти.

Главное от этого убрать: Разделить обработку на управляемые куски, я бы сказал, не более 1000 одновременно. Вы можете загрузить свой словарь строк, но разбить его на 1000, где вызывается метод для обработки каждой 1000 в новом DbContext, а не в одном контексте для всех записей. Если вы хотите иметь возможность надежно откатить изменения в случае сбоя одной партии из 1000, я бы предложил либо явную транзакцию (более безопасную, но более медленную) или использование столбца маркера в таблицах, чтобы указать, что они находятся в состоянии ожидания. Там, где вам нужно отслеживать записи, которые были успешно импортированы или у которых есть проблемы, я бы порекомендовал использовать списки идентификаторов, а не списки целых объектов для экономии памяти.

0 голосов
/ 19 апреля 2019

Вы используете много словарей, и каждый из них требует много памяти.Каждый раз, когда Dictionary исчерпывает свой внутренний буфер, он должен создать новый, а размер измененного буфера более чем в два раза превышает предыдущий.Это происходит потому, что этот размер должен быть простым числом.Внутри есть список с предварительно вычисленными простыми числами, но этот список заканчивается максимумом 7199369. За этой точкой следующее простое число вычисляется методом грубой силы.Так что, возможно, вы попали в опасную зону, где все уже неэффективно.

Исходный код Dictionary.Resize

Исходный код HashHelpers.GetPrime

Исходный код HashHelpers.primes

public static readonly int[] primes = {
    3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131,
    163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
    1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861,
    5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023,
    25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523,
    108631, 130363, 156437, 187751, 225307, 270371, 324449,
    389357, 467237, 560689, 672827, 807403, 968897, 1162687,
    1395263, 1674319, 2009191, 2411033, 2893249, 3471899,
    4166287, 4999559, 5999471, 7199369};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...