遍历字符串列表中的大量数据会导致Visual Studio C#

时间:2019-04-19 16:45:05

标签: c# entity-framework

我正在实现一个网页功能,该功能可以同时将大量数据(3x10 ^ 5行左右)上传到数据库中的不同表。用户将使用此数据生成一个excel文件,并将其上传到服务器。我正在使用C#MVC构建网页,并使用“ ExcelDataReader”库读取Excel文件。开始时,我使用“ .AsDataSet”方法检索数据,但在我的场景中读取DataTable极其缓慢且内存效率低下,因此我创建了自己的函数,该函数读取每行一行并将所有内容存储在列表的列表中。字符串。由于我正在处理许多与数据库中其他表具有一对多关系的表,因此我使用字典来存储在此过程中创建的实体,因此如果以后需要它们的话,检索它们将变得更加容易。从理论上讲,我的for循环中的所有操作都是O(1),所以我不明白两件事:

-花超过五分钟的时间循环遍历所有行的一半 -当达到一半时,我的视觉工作室就崩溃了,没有任何消息。

我正在使用Visual Studio 2015,我的PC统计信息为:i5 6500 +内存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 个答案:

答案 0 :(得分:1)

有很多事情可能会破坏性能。一次击中约300万行绝不是一个好主意。首先,您需要为所有集合和词典保留大量内存以供处理。然后,事实是DbContext对整个操作都是打开的,因此,也跟踪装入或关联到该上下文的每个单个实体。打开上下文的时间越长,跟踪的实体越多,事情就变得越慢。

接下来还有其他一些小细节可以帮助降低性能。仅检查实体是否存在而进行FirstOrDefault是完全的性能浪费。使用.Any

即 if(!context.Matricula.Any(x => x.alumnoId ==辅助&& x.seccionId ==辅助2);

FirstOrDefault返回实体数据或#null,Any执行查询,如果该实体存在,则仅返回True或False。 =更快,更少的内存浪​​费。

要摆脱的主要问题是: 将处理分为可管理的块,一次最多只能说1000个。您可以加载行字典,但将其拆分为1000,在其中调用一种方法来处理新DbContext中的每1000个而不是所有记录中的一个上下文。如果您希望能够可靠地回滚更改(如果一批1000次失败),则建议使用显式事务(更安全,但速度更慢)或使用表上的标记列来指示它们处于挂起状态。在需要跟踪已成功导入的记录或需要解决的问题的地方,建议您使用ID列表而不是整个实体列表来节省内存。

答案 1 :(得分:0)

您正在使用许多词典,每个词典都需要大量内存。每次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};