使用C# Core从数据库生成Dfs2文件的数据绑定问题排查
我从数据库获取了45列72行的数据,需要生成5列9行的Dfs2文件(72行对应72小时)。文件可以正常生成,但数据和之前用Python生成的正确Dfs2文件不一致,请帮忙排查我的C#代码中数据绑定是否存在问题。
我的C#代码
DataTable ecmwfWsData = new DataTable(); NpgsqlCommand cmd = new NpgsqlCommand(sqlSelectQuery, connection); NpgsqlDataAdapter adp = new NpgsqlDataAdapter(cmd); adp.Fill(ecmwfWsData); string[] itemNames ={ "ECMWFC0000Ctrl", "ECMWFC0001Ctrl", "ECMWFC0002Ctrl", "ECMWFC0003Ctrl", "ECMWFC0004Ctrl", "ECMWFC0100Ctrl", "ECMWFC0101Ctrl", "ECMWFC0102Ctrl", "ECMWFC0103Ctrl", "ECMWFC0104Ctrl", "ECMWFC0200Ctrl", "ECMWFC0201Ctrl", "ECMWFC0202Ctrl", "ECMWFC0203Ctrl", "ECMWFC0204Ctrl", "ECMWFC0300Ctrl", "ECMWFC0301Ctrl", "ECMWFC0302Ctrl", "ECMWFC0303Ctrl", "ECMWFC0304Ctrl", "ECMWFC0400Ctrl", "ECMWFC0401Ctrl", "ECMWFC0402Ctrl", "ECMWFC0403Ctrl", "ECMWFC0404Ctrl", "ECMWFC0500Ctrl", "ECMWFC0501Ctrl", "ECMWFC0502Ctrl", "ECMWFC0503Ctrl", "ECMWFC0504Ctrl", "ECMWFC0600Ctrl", "ECMWFC0601Ctrl", "ECMWFC0602Ctrl", "ECMWFC0603Ctrl", "ECMWFC0604Ctrl", "ECMWFC0700Ctrl", "ECMWFC0701Ctrl", "ECMWFC0702Ctrl", "ECMWFC0703Ctrl", "ECMWFC0704Ctrl", "ECMWFC0800Ctrl", "ECMWFC0801Ctrl", "ECMWFC0802Ctrl", "ECMWFC0803Ctrl", "ECMWFC0804Ctrl" }; if (ecmwfWsData.Rows.Count == 0) { return $"Data is not present for FRDate: {frDate} and FRun: {fRun}"; } else { double x0 = 79.89999974583402; double y0 = 12.500001526228607; string[] dfsCoordinate = { "LONG/LAT", x0.ToString(), y0.ToString(), "0" }; double dfsDx = 0.1; double dfsDy = 0.1; int dfsDt = 3600; var fDTime = ""; var frdate = $"{frDate.Substring(0, 4)}-{frDate.Substring(4, 2)}-{frDate.Substring(6, 2)}"; DateTime date = Convert.ToDateTime(frdate); if (fRun.Equals("R00")) { fDTime = date.ToString("dd-MM-yyyy 06:30:00"); } else if (fRun.Equals("R06")) { fDTime = date.ToString("dd-MM-yyyy 12:30:00"); } else if (fRun.Equals("R12")) { fDTime = date.ToString("dd-MM-yyyy 18:30:00"); } else if (fRun.Equals("R18")) { date = date.AddDays(1); fDTime = date.ToString("dd-MM-yyyy 00:30:00"); } DateTime dfsstarttime = DateTime.ParseExact(fDTime, "dd-MM-yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); double[,,] ecmwfGridData = new double[72, 9, 5]; int ni = 0; foreach (DataRow row in ecmwfWsData.Rows) { int columnIndex = 0; for (int i = 0; i < 9; i++) { for (int j = 0; j < 5; j++) { ecmwfGridData[ni, i, j] = Math.Round((double)Convert.ToDouble(row.Field<float>(columnIndex)), 1); columnIndex++; } } ni++; } DfsFactory factory = new DfsFactory(); Dfs2Builder builder = Dfs2Builder.Create("ECMWFGridData", "dfs Timeseries Bridge", 0); builder.SetDataType(1); builder.SetGeographicalProjection(factory.CreateProjectionGeoOrigin("UTM-33", 12.438741600559766, 55.225707842436385, 326.99999999999955)); builder.SetTemporalAxis(factory.CreateTemporalEqCalendarAxis(eumUnit.eumUminute, dfsstarttime, 0, 60)); builder.SetSpatialAxis(factory.CreateAxisEqD2(eumUnit.eumUmeter, ecmwfGridData.GetLength(2), 0, dfsDy, ecmwfGridData.GetLength(1), 0, dfsDx)); builder.DeleteValueDouble = -1e-30; builder.AddCustomBlock(factory.CreateCustomBlock("M21_Misc", new double[] { 327, 0.2, -900, 10, 0, 0, 0 })); foreach (var item in itemNames) { builder.AddDynamicItem(item, eumQuantity.Create(eumItem.eumIRainfall, eumUnit.eumUmillimeter), DfsSimpleType.Double, DataValueType.Instantaneous); } string dfsfilename = Path.Combine($"D:\\DFS2", $"AllWSDFS_{frDate}_{fRun}_CtrlFile.dfs2"); builder.CreateFile(dfsfilename); Dfs2File file = builder.GetFile(); for (int i = 0; i < 72; i++) { for (int j = 0; j < itemNames.Length; j++) { IDfsItemData2D<double> itemData2D = (IDfsItemData2D<double>)file.CreateEmptyItemData(j + 1); double[,] transposedData = new double[ecmwfGridData.GetLength(2), ecmwfGridData.GetLength(1)]; for (int k = 0; k < ecmwfGridData.GetLength(2); k++) { for (int l = 0; l < ecmwfGridData.GetLength(1); l++) { transposedData[k, l] = ecmwfGridData[i, l, k]; } } int totalElements = transposedData.GetLength(0) * transposedData.GetLength(1); double[] data = new double[totalElements]; int index = 0; for (int k = 0; k < transposedData.GetLength(0); k++) { for (int l = 0; l < transposedData.GetLength(1); l++) { data[index++] = Math.Round(transposedData[k, l], 1); } } file.WriteItemTimeStep(j + 1, i, 60, data); } } file.Close(); }
核心排查点
数据填充的维度顺序
你的ecmwfGridData是[72,9,5](时间步、行、列),数据库每行45列对应9*5的网格。需确认数据库的45列是否严格按「9组,每组5列」的顺序排列——如果实际是「5组,每组9列」,当前的i(行)外层、j(列)内层循环会直接导致行列数据错位。数据写入的扁平化顺序错误
Dfs2要求一维数据按**行优先(先遍历所有列,再换下一行)**排列,即顺序为[行0列0, 行0列1,...,行0列4,行1列0,...,行8列4]。但你当前的转置+扁平化逻辑是按列优先遍历,生成的顺序是[列0行0,列0行1,...,列0行8,列1行0,...,列4行8],这是数据错位的核心原因。修正方案:跳过转置步骤,直接按行优先顺序生成一维数组:
int totalElements = 9 * 5; double[] data = new double[totalElements]; int index = 0; for (int l = 0; l < 9; l++) // 先遍历行 { for (int k = 0; k < 5; k++) // 再遍历列 { data[index++] = Math.Round(ecmwfGridData[i, l, k], 1); } }空间轴参数匹配
CreateAxisEqD2的参数顺序是(单位, nx, x0, dx, ny, y0, dy),其中nx是列数、ny是行数。你当前用ecmwfGridData.GetLength(2)(5)作为nx,GetLength(1)(9)作为ny是正确的,但需确认Python版本是否用了完全一致的空间轴参数,否则会导致数据空间映射错误。时间轴与时间步一致性
确认Python版本的时间轴起始时间、步长和你的代码完全匹配——当前代码时间步长为60分钟,写入时传递的步长参数也是60,若Python版本用了不同的时间配置,会导致时间步对应的数据错位。
内容的提问来源于stack exchange,提问作者Sidhant Priyadarshan Biswal

