const int _upload_batchSize = 1000; // 上传结果集每批个数 // 写入 dp2kernel 成为永久结果集 int UploadPermanentResultset(RmsChannel channel, string strResultsetName, DpResultSet resultset, out string strError) { strError = ""; // resultset.Clear(); // testing List<RecordBody> records = new List<RecordBody>(); // 结果集为空,也要在 dp2kernel 端创建一个结果集对象 if (resultset.Count == 0) { RecordBody[] results = null; long lRet = channel.DoWriteRecords(null, records.ToArray(), "createResultset,name:#" + strResultsetName + ",clear,permanent", out results, out strError); if (lRet == -1) return -1; return 0; } #if DETAIL_LOG this.WriteErrorLog("开始上传结果集到 dp2kernel"); #endif long lPos = -1; // 中间保持不透明的值 bool bFirst = true; for (long i = 0; i < resultset.Count; i++) { DpRecord dpRecord = null; long lIndex = i; if (lIndex == 0) { dpRecord = resultset.GetFirstRecord( lIndex, false, out lPos); } else { // 取元素比[]操作速度快 dpRecord = resultset.GetNextRecord( ref lPos); } if (dpRecord == null) break; RecordBody record = new RecordBody(); record.Path = dpRecord.ID; records.Add(record); if (records.Count >= _upload_batchSize || (i >= resultset.Count - 1 && records.Count > 0)) { this._app_down.Token.ThrowIfCancellationRequested(); #if DETAIL_LOG this.WriteErrorLog("一批 " + records.Count); #endif RecordBody[] results = null; long lRet = channel.DoWriteRecords(null, records.ToArray(), "createResultset,name:#~" + strResultsetName + (bFirst ? ",clear" : ""), out results, out strError); if (lRet == -1) return -1; bFirst = false; records.Clear(); } } // 最后一次执行改名、排序 { RecordBody[] results = null; long lRet = channel.DoWriteRecords(null, null, "renameResultset,oldname:#~" + strResultsetName + ",newname:#" + strResultsetName + ",permanent,sort", out results, out strError); if (lRet == -1) return -1; } #if DETAIL_LOG this.WriteErrorLog("结束上传结果集"); #endif return 0; }
// 将 XML 记录成批写入数据库 // return: // -1 出错 // >=0 本次已经写入的记录个数。本函数返回时 records 集合的元素数没有变化(但元素的Path和Timestamp会有变化),如果必要调主可截取records集合中后面未处理的部分再次调用本函数 public static int WriteRecords( IWin32Window owner, Stop stop, RmsChannel channel, bool bQuickMode, bool bIfNotExist, List<UploadRecord> records, // List<RecordBody> records, ref bool bDontPromptTimestampMismatchWhenOverwrite, out string strError) { strError = ""; // int nRet = 0; int nProcessCount = 0; // TODO: 需要检查每个 Path 的服务器URL是完全一样的 // 提交 RecordBody[] inputs = new RecordBody[records.Count]; for (int i = 0; i < records.Count; i++) { UploadRecord record = records[i]; inputs[i] = record.RecordBody; Debug.Assert(record.RecordBody != null, ""); } List<string> styles = new List<string>(); if (bQuickMode) styles.Add("fastmode"); if (bIfNotExist) styles.Add("ifnotexist"); // 记录不存在才决定写入 string strStyle = StringUtil.MakePathList(styles); REDO: RecordBody[] results = null; long lRet = channel.DoWriteRecords(stop, inputs, strStyle, // bQuickMode == true ? "fastmode" : "", // strStyle, out results, out strError); if (lRet == -1) return -1; if (results == null) { strError = "results == null"; return -1; } // TODO: 如何面对 Server 的 Quota Exceed 报错? // 出错后改用只提交一个记录的策略?或者更换为传统的保存方式? Debug.Assert(results.Length <= inputs.Length); List<RecordBody> redo_write_list = new List<RecordBody>(); // 需要重做写入的元素 List<RecordBody> redo_timestamp_list = new List<RecordBody>(); // 因为时间戳不匹配,需要重做写入的元素 // 询问时间戳不匹配等报错 string strMessageError = ""; string strMessageTimestamp = ""; for (int i = 0; i < results.Length; i++) { RecordBody result = results[i]; if (result.Result != null && result.Result.ErrorCode != ErrorCodeValue.NoError) { if (bIfNotExist && result.Result.ErrorCode == ErrorCodeValue.Canceled) { // ifnotexist 情况下不要报错 nProcessCount++; continue; } if (result.Result.ErrorCode == ErrorCodeValue.TimestampMismatch) { strMessageTimestamp += "记录 " + result.Path + " 在覆盖保存过程中出错: " + result.Result.ErrorString + "\r\n"; redo_timestamp_list.Add(inputs[i]); } else { strMessageError += "记录 " + result.Path + " 保存过程中出错: " + result.Result.ErrorString + "\r\n"; redo_write_list.Add(inputs[i]); } } else { nProcessCount++; } } if (string.IsNullOrEmpty(strMessageTimestamp) == false) { if (bDontPromptTimestampMismatchWhenOverwrite == false) { DialogResult result = MessageDlg.Show(owner, strMessageTimestamp + "\r\n---\r\n\r\n是否重试以新时间戳强行覆盖保存?\r\n\r\n注:\r\n[重试] 重试强行覆盖\r\n[跳过] 忽略当前记录或资源保存,但继续后面的处理\r\n[中断] 中断整个批处理", "导入数据", MessageBoxButtons.YesNoCancel, MessageBoxDefaultButton.Button1, ref bDontPromptTimestampMismatchWhenOverwrite, new string[] { "重试", "跳过", "中断" }); if (result == DialogResult.Cancel) { strError = strMessageTimestamp; return -1; } if (result == DialogResult.No) { // 跳过 redo_timestamp_list.Clear(); if (redo_timestamp_list.Count + redo_write_list.Count == 0) return results.Length; } } } if (string.IsNullOrEmpty(strMessageError) == false) { bool bDontAsk = false; DialogResult result = MessageDlg.Show(owner, strMessageError + "\r\n---\r\n\r\n是否重试保存?\r\n\r\n注:\r\n[重试] 重试保存\r\n[跳过] 忽略当前记录或资源保存,但继续后面的处理\r\n[中断] 中断整个批处理", "导入数据", MessageBoxButtons.YesNoCancel, MessageBoxDefaultButton.Button1, ref bDontAsk, new string[] { "重试", "跳过", "中断" }); if (result == DialogResult.Cancel) { strError = strMessageError; return -1; } if (result == DialogResult.No) { // 跳过 redo_write_list.Clear(); if (redo_timestamp_list.Count + redo_write_list.Count == 0) return results.Length; } } // for (int i = 0; i < results.Length; i++) { RecordBody result = results[i]; RecordBody record = inputs[i]; record.Path = result.Path; // 实际写入的路径 record.Timestamp = result.Timestamp; // 新的时间戳 } if (redo_write_list.Count + redo_timestamp_list.Count > 0) { inputs = new RecordBody[redo_write_list.Count + redo_timestamp_list.Count]; redo_write_list.CopyTo(inputs); redo_timestamp_list.CopyTo(inputs, redo_write_list.Count); goto REDO; } return nProcessCount; }