c# - 循环遍历字符串列表中的大量数据崩溃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++;
}
解决方案
有很多事情可能会影响性能。一次点击循环超过 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 列表而不是整个实体的列表来节省内存。
推荐阅读
- visual-studio - 通过 Device Portal 或 Visual Studio 将大型应用程序旁加载到 Xbox One 时一直失败……为什么?
- java - 如何在 Spring 中实现视频导出器?
- javascript - 如何保持 Material-UI 表格可滚动并具有动态高度?
- java - 如何通过 NFC 阅读器从 GooglePay/ApplePay 获取有效的信用卡号?
- java - DateTimeFormatter.parse() 更正日期信息而不是抛出异常
- python - 在附加到列表之前过滤字典
- c# - 我应该将什么传递给 SQLitePCL.raw.SetProvider() 以避免“'Microsoft.Data.Sqlite.SqliteConnection' 的类型初始化程序引发异常”
- python - 您是否有理由不能按列表中的元素/值名称使用 pop() 方法?
- eclipse - 怎么去掉这个红框
- angular - 泳道 ngx 折线图第二个 y 轴角度