首页 > 解决方案 > 循环遍历字符串列表中的大量数据崩溃Visual Studio C#

问题描述

我正在为网页实现一项功能,该功能将同时将大量数据(大约 3x10^5 行)上传到数据库中的不同表。用户将使用此数据生成一个 excel 文件并将其上传到服务器。我正在使用 C# MVC 构建网页和“ExcelDataReader”库来读取 Excel 文件。一开始,我使用“.AsDataSet”方法来检索数据,但在我的场景中读取 DataTable 非常慢且内存效率低下,因此我创建了自己的函数,每行读取行并将所有内容存储在列表列表中字符串。由于我正在处理与我的数据库中的其他表具有一对多关系的许多表,因此我使用字典来存储在此过程中创建的实体,因此如果以后需要它们,检索它们会更容易。

- 循环所有行的一半需要超过五分钟 - 当它到达半点时,我的视觉工作室只是崩溃而没有任何消息。

我正在使用 Visual Studio 2015,我的 PC 统计数据是: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++;

}

标签: c#entity-framework

解决方案


有很多事情可能会影响性能。一次点击循环超过 300 万行绝不是一个好主意。首先,您需要为所有集合和字典保留大量内存以进行处理。然后是 DbContext 为整个操作打开的事实,因此加载或关联到上下文的每个实体也被跟踪。上下文打开的时间越长,跟踪的实体越多,事情就会变得越慢。

接下来还有其他一些有助于扼杀性能的小细节。仅仅FirstOrDefault检查实体是否存在是完全的性能浪费。使用.Any.

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

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

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


推荐阅读