// 开始监控一个门 public void BeginMonitor(DoorItem door) { lock (_syncRoot) { // 检查以前这个门是否已经有监控消息 var message = _messages.Find((m) => { if (m.Door == door) { return(true); } return(false); }); if (message != null) { App.CurrentApp.Speak("补做遗留提交"); WpfClientInfo.WriteInfoLog($"对门 {message.Door.Name} 补做一次 submit (1)"); _func_submit?.Invoke(message.Door, false); message.StartTime = DateTime.Now; // 重新开始 message.HeartbeatTicks = RfidManager.LockHeartbeat; } else { message = new MonitorMessage { Door = door, StartTime = DateTime.Now, HeartbeatTicks = RfidManager.LockHeartbeat }; _messages.Add(message); } } }
// 处理一次监控任务 public void ProcessTimeout() { lock (_syncRoot) { List <MonitorMessage> delete_messages = new List <MonitorMessage>(); foreach (var message in _messages) { // if (DateTime.Now - message.StartTime > TimeoutLength) if (RfidManager.LockHeartbeat - message.HeartbeatTicks >= 2) { App.CurrentApp.Speak("超时补做提交"); WpfClientInfo.WriteInfoLog($"超时情况下,对门 {message.Door.Name} 补做一次 submit"); _func_submit?.Invoke(message.Door, true); delete_messages.Add(message); message.Door.Waiting--; } } foreach (var message in delete_messages) { _messages.Remove(message); } } }
// 把 Actions 追加保存到本地数据库 // 当本函数执行完以后,ActionInfo 对象的 ID 有了值,和数据库记录的 ID 对应 public static async Task SaveActionsToDatabaseAsync(List <ActionInfo> actions) { try { using (var releaser = await _databaseLimit.EnterAsync()) { using (var context = new RequestContext()) { context.Database.EnsureCreated(); var requests = FromActions(actions); foreach (var request in requests) { // 注:这样一个一个保存可以保持 ID 的严格从小到大。因为这些事项之间是有严格顺序关系的(借和还顺序不能颠倒) await context.Requests.AddRangeAsync(request); int nCount = await context.SaveChangesAsync(); // 2020/4/27 // 如果是还书动作,需要更新它之前的全部同 PII 的借书动作的 LinkID 字段 if (request.Action == "return" || request.Action == "inventory") { var borrowRequests = context.Requests. Where(o => o.PII == request.PII && o.Action == "borrow" && o.LinkID == null) .ToList(); foreach (var item in borrowRequests) { item.LinkID = request.ID.ToString(); context.Requests.Update(item); await context.SaveChangesAsync(); } } } Debug.Assert(requests.Count == actions.Count, ""); // 刷新 ActionInfo 对象的 ID for (int i = 0; i < requests.Count; i++) { actions[i].ID = requests[i].ID; } WpfClientInfo.WriteInfoLog($"Actions 保存到本地数据库成功。内容如下:\r\n{ActionInfo.ToString(actions)}"); } } } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"SaveActionsToDatabase() 出现异常: {ExceptionUtil.GetDebugText(ex)}"); throw ex; } }
// sync/commerror/normalerror/空 // 同步成功/通讯出错/一般出错/从未同步过 // 从外部存储中装载尚未同步的 Actions // 注意:这些 Actions 应该先按照 PII 排序分组以后,一组一组进行处理 public static async Task <List <ActionInfo> > LoadRetryActionsFromDatabaseAsync() { using (var releaser = await _databaseLimit.EnterAsync()) { using (var context = new RequestContext()) { context.Database.EnsureCreated(); var items = context.Requests.Where(o => o.State != "sync" && o.State != "dontsync") .OrderBy(o => o.ID).ToList(); var actions = FromRequests(items); WpfClientInfo.WriteInfoLog($"从本地数据库装载 Actions 成功。内容如下:\r\n{ActionInfo.ToString(actions)}"); return(actions); } } }
// 启动盘点后台任务 public static void StartInventoryTask() { if (_inventoryTask != null) { return; } CancellationToken token = App.CancelToken; token.Register(() => { _eventInventory.Set(); }); _inventoryTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("盘点后台线程开始"); try { while (token.IsCancellationRequested == false) { // await Task.Delay(TimeSpan.FromSeconds(10)); _eventInventory.WaitOne(_inventoryIdleLength); token.ThrowIfCancellationRequested(); // await ProcessingAsync(); } _inventoryTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"盘点后台线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("inventory_worker", $"盘点后台线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("盘点后台线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
// 启动同步任务。此任务长期在后台运行 public static void StartSyncTask() { if (_syncTask != null) { return; } CancellationToken token = _cancel.Token; token.Register(() => { _eventSync.Set(); }); // 启动重试专用线程 _syncTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("重试专用线程开始"); try { while (token.IsCancellationRequested == false) { // TODO: 无论是整体退出,还是需要激活,都需要能中断 Delay // Task.Delay(TimeSpan.FromSeconds(10)).Wait(token); _eventSync.WaitOne(_syncIdleLength); token.ThrowIfCancellationRequested(); #if REMOVED // 顺便检查和确保连接到消息服务器 App.CurrentApp.EnsureConnectMessageServer().Wait(token); #endif #if REMOVED // 顺便关闭天线射频 if (_tagAdded) { _ = Task.Run(async() => { try { await SelectAntennaAsync(); } catch { // TODO: 写入错误日志 } }); _tagAdded = false; } #endif if (PauseSubmit) { continue; } // 2020/6/21 if (ShelfData.LibraryNetworkCondition != "OK") { continue; } // TODO: 从本地数据库中装载需要同步的那些 Actions List <ActionInfo> actions = await LoadRetryActionsFromDatabaseAsync(); if (actions.Count == 0) { continue; } // RefreshRetryInfo() ??? // 一般来说,只要 SubmitWindow 开着,就要显示请求情况结果。 // 特殊地,如果 SubmitWindow 没有开着,但本次至少有一个成功的请求结果了,那就专门打开 SubmitWindow 显示信息 int succeedCount = 0; // 同步成功的事项数量 int newCount = 0; // 首次进行同步的事项数量 // 排序和分组。按照分组提交给 dp2library 服务器 // TODO: 但进度显示不应该太细碎?应该按照总的进度来显示 var groups = GroupActions(actions); // List<MessageItem> messages = new List<MessageItem>(); // 准备对话框 // SubmitWindow progress = PageMenu.PageShelf?.OpenProgressWindow(); SubmitWindow progress = PageMenu.PageShelf?.ProgressWindow; int start = 0; // 当前 group 开始的偏移 int total = actions.Count; foreach (var group in groups) { int current_count = group.Count; // 当前 group 包含的动作数量 var result = await SubmitCheckInOutAsync( (min, max, value, text) => { // 2020/4/2 // 修正三个值 if (max != -1) { max = total; } //if (min != -1) // min += start; if (value != -1) { value += start; } if (progress != null) { App.Invoke(new Action(() => { if (min == -1 && max == -1 && value == -1 && groups.IndexOf(group) == groups.Count - 1) // 只有最后一次才隐藏进度条 { progress.ProgressBar.Visibility = Visibility.Collapsed; } else { progress.ProgressBar.Visibility = Visibility.Visible; } if (text != null) { progress.TitleText = text; // + " " + (progress.Tag as string); } if (min != -1) { progress.ProgressBar.Minimum = min; } if (max != -1) { progress.ProgressBar.Maximum = max; } if (value != -1) { progress.ProgressBar.Value = value; } })); } }, group, "auto_stop"); // TODO: 把 group 中报错的信息写入本地数据库的对应事项中 /* * // 把已经处理成功的 Action 对应在本地数据库中的事项的状态修改 * List<ActionInfo> processed = new List<ActionInfo>(); * if (result.RetryActions != null) * { * foreach (var action in group) * { * if (result.RetryActions.IndexOf(action) == -1) * { * ChangeDatabaseActionState(action.ID, "sync"); * processed.Add(action); * } * } * } */ if (result.ProcessedActions != null) { // result.ProcessedActions.ForEach(o => { if (o.SyncCount == 0) newCount++; }); foreach (var action in result.ProcessedActions) { if (action.State == "sync") { succeedCount++; } if (action.SyncCount == 1) { newCount++; } // sync/commerror/normalerror/空 // 同步成功/通讯出错/一般出错/从未同步过 await ChangeDatabaseActionStateAsync(action.ID, action); } // TODO: 通知消息正文是否也告知一下同一个 PII 后面有多少个动作被跳过处理? MessageNotifyOverflow(result.ProcessedActions); } if (progress != null && progress.IsVisible) { // 根据全部和已处理集合得到未处理(被跳过的)集合 var skipped = GetSkippedActions(group, result.ProcessedActions); foreach (var action in skipped) { action.State = "_"; action.SyncErrorCode = "skipped"; // action.SyncErrorInfo = "暂时跳过同步"; } List <ActionInfo> display = new List <ActionInfo>(result.ProcessedActions); display.AddRange(skipped); // Thread.Sleep(3000); // 刷新显示 App.Invoke(new Action(() => { progress?.Refresh(display); })); } /* * if (result.MessageDocument != null) * messages.AddRange(result.MessageDocument.Items); */ start += current_count; } // TODO: 更新每个事项的 RetryCount。如果超过 10 次,要把 State 更新为 fail // 将 submit 情况写入日志备查 // WpfClientInfo.WriteInfoLog($"重试提交请求:\r\n{ActionInfo.ToString(actions)}\r\n返回结果:{result.ToString()}"); #if REMOVED // 如果本轮有成功的请求,并且进度窗口没有打开,则补充打开进度窗口 if ((progress == null || progress.IsVisible == false) && succeedCount > 0) { progress = PageMenu.PageShelf?.OpenProgressWindow(); } // 把执行结果显示到对话框内 // 全部事项都重试失败的时候不需要显示 if (progress != null && progress.IsVisible && (succeedCount > 0 || newCount > 0)) { Application.Current.Dispatcher.Invoke(new Action(() => { MessageDocument doc = new MessageDocument(); doc.AddRange(messages); progress?.PushContent(doc); })); // 显示出来 Application.Current.Dispatcher.Invoke(new Action(() => { progress?.ShowContent(); })); } #endif } _syncTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"重试专用线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("sync", $"重试专用线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("重试专用线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
// 启动一般监控任务 public static void StartMonitorTask() { if (_monitorTask != null) { return; } PerdayTask.StartPerdayTask(); CancellationToken token = _cancel.Token; // bool download_complete = false; token.Register(() => { _eventMonitor.Set(); }); _monitorTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("书柜监控专用线程开始"); try { while (token.IsCancellationRequested == false) { // await Task.Delay(TimeSpan.FromSeconds(10)); _eventMonitor.WaitOne(_monitorIdleLength); token.ThrowIfCancellationRequested(); // *** // 关闭天线射频 if (_tagAdded) { _ = Task.Run(async() => { try { await SelectAntennaAsync(); } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"关闭天线射频 SelectAntennaAsync() 时出现异常: {ExceptionUtil.GetDebugText(ex)}"); } }); _tagAdded = false; } if (DateTime.Now - _lastDetectTime > _detectPeriod) { DetectLibraryNetwork(); _lastDetectTime = DateTime.Now; } // 提醒关门 WarningCloseDoor(); // 下载或同步读者信息 string startDate = LoadStartDate(); if (/*download_complete == false || */ string.IsNullOrEmpty(startDate) && _replicatePatronError == 0) { // 如果 Config 中没有记载断点位置,说明以前从来没有首次同步过。需要进行一次首次同步 if (string.IsNullOrEmpty(startDate)) { // SaveStartDate(""); var repl_result = await PatronReplication.DownloadAllPatronRecordAsync( (text) => { WpfClientInfo.WriteInfoLog(text); PageShelf.TrySetMessage(null, text); }, token); if (repl_result.Value == -1) { // TODO: 判断通讯出错的错误码。如果是通讯出错,则稍后需要重试下载 _replicatePatronError++; } else { SaveStartDate(repl_result.StartDate); } // 立刻允许接着做一次零星同步 ActivateMonitor(); } // download_complete = true; } else { // 进行零星同步 if (DateTime.Now - _lastReplicateTime > _replicatePeriod) { // string startDate = LoadStartDate(); // testing // startDate = "20200507:0-"; if (string.IsNullOrEmpty(startDate) == false) { string endDate = DateTimeUtil.DateTimeToString8(DateTime.Now); // parameters: // strLastDate 处理中断或者结束时返回最后处理过的日期 // last_index 处理或中断返回时最后处理过的位置。以后继续处理的时候可以从这个偏移开始 // return: // -1 出错 // 0 中断 // 1 完成 ReplicationResult repl_result = await PatronReplication.DoReplication( startDate, endDate, LogType.OperLog, token); if (repl_result.Value == -1) { WpfClientInfo.WriteErrorLog($"同步出错: {repl_result.ErrorInfo}"); } else if (repl_result.Value == 1) { string lastDate = repl_result.LastDate + ":" + repl_result.LastIndex + "-"; // 注意 - 符号不能少。少了意思就会变成每次只获取一条日志记录了 SaveStartDate(lastDate); } _lastReplicateTime = DateTime.Now; } } } } _monitorTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"书柜监控专用线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("shelf_monitor", $"书柜监控专用线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("书柜监控专用线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
// 从 _entityList 中取出一批事项进行处理。由于是复制出来处理的,所以整个处理过程中(除了取出和最后删除的瞬间)不用对 _entityList 加锁 // 对每一个事项,要进行如下处理: // 1) 获得册记录和书目摘要 // 2) 尝试请求还书 // 3) 请求设置 UID // 4) 修改 currentLocation 和 location static async Task ProcessingAsync() { var list = CopyList(); foreach (var state in list) { // state.State = "???"; try { if (state.NewState == "open") { continue; } Debug.Assert(state.NewState == "close"); List <ActionInfo> actions = null; try { DateTime start = DateTime.Now; var result = await ShelfData.RefreshInventoryAsync(state.Door); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 RefreshInventoryAsync() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); start = DateTime.Now; // 2020/4/21 把这两句移动到 try 范围内 await SaveDoorActions(state.Door, true); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 SaveDoorActions() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); start = DateTime.Now; actions = ShelfData.PullActions(); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 PullActions() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); } finally { state.Door.DecWaiting(); //WpfClientInfo.WriteInfoLog($"--decWaiting() door '{e.Door.Name}' state changed"); } // 注: 调用完成后门控件上的 +- 数字才会消失 var task = PageMenu.PageShelf.DoRequestAsync(actions, ""); App.SetError("processing", null); } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"ProcessingAsync() 出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("processing", $"ProcessingAsync() 出现异常: {ex.Message}"); } finally { // state.State = ""; } } // 把处理过的 entity 从 list 中移走 RemoveList(list); // 2020/11/21 // 如果发现队列里面又有新的对象,则立即激活任务 if (GetListCount() > 0) { ActivateTask(); } }
// 启动一般监控任务 public static void StartMonitorTask() { if (_monitorTask != null) { return; } CancellationToken token = App.CancelToken; token.Register(() => { _eventMonitor.Set(); }); _monitorTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("SIP 监控专用线程开始"); try { while (token.IsCancellationRequested == false) { // await Task.Delay(TimeSpan.FromSeconds(10)); _eventMonitor.WaitOne(_monitorIdleLength); token.ThrowIfCancellationRequested(); if (DateTime.Now - _lastDetectTime > _detectPeriod) { var detect_result = await DetectSipNetworkAsync(); _lastDetectTime = DateTime.Now; // testing //detect_result.Value = -1; //detect_result.ErrorInfo = "测试文字"; if (detect_result.Value != 1) { App.OpenErrorWindow(detect_result.ErrorInfo); } else { App.CloseErrorWindow(); } } } _monitorTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"SIP 监控专用线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("monitor", $"SIP 监控专用线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("SIP 监控专用线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
// 启动一般监控任务 public static void StartMonitorTask() { if (_monitorTask != null) { return; } CancellationToken token = _cancel.Token; bool download_complete = false; token.Register(() => { _eventMonitor.Set(); }); _monitorTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("监控专用线程开始"); try { while (token.IsCancellationRequested == false) { // await Task.Delay(TimeSpan.FromSeconds(10)); _eventMonitor.WaitOne(_monitorIdleLength); token.ThrowIfCancellationRequested(); // *** // 关闭天线射频 if (_tagAdded) { _ = Task.Run(async() => { try { await SelectAntennaAsync(); } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"关闭天线射频 SelectAntennaAsync() 时出现异常: {ExceptionUtil.GetDebugText(ex)}"); } }); _tagAdded = false; } if (DateTime.Now - _lastDetectTime > _detectPeriod) { DetectLibraryNetwork(); _lastDetectTime = DateTime.Now; } // 提醒关门 WarningCloseDoor(); // 下载或同步读者信息 string startDate = LoadStartDate(); if (/*download_complete == false || */ string.IsNullOrEmpty(startDate)) { // 如果 Config 中没有记载断点位置,说明以前从来没有首次同步过。需要进行一次首次同步 if (string.IsNullOrEmpty(startDate)) { // SaveStartDate(""); var repl_result = await PatronReplication.DownloadAllPatronRecordAsync(token); if (repl_result.Value == -1) { // TODO: 判断通讯出错的错误码。如果是通讯出错,则稍后需要重试下载 } else { SaveStartDate(repl_result.StartDate); } // 立刻允许接着做一次零星同步 ActivateMonitor(); } // download_complete = true; } else { // 进行零星同步 if (DateTime.Now - _lastReplicateTime > _replicatePeriod) { // string startDate = LoadStartDate(); // testing // startDate = "20200507:0-"; if (string.IsNullOrEmpty(startDate) == false) { string endDate = DateTimeUtil.DateTimeToString8(DateTime.Now); // parameters: // strLastDate 处理中断或者结束时返回最后处理过的日期 // last_index 处理或中断返回时最后处理过的位置。以后继续处理的时候可以从这个偏移开始 // return: // -1 出错 // 0 中断 // 1 完成 ReplicationResult repl_result = PatronReplication.DoReplication( startDate, endDate, LogType.OperLog, token); if (repl_result.Value == -1) { WpfClientInfo.WriteErrorLog($"同步出错: {repl_result.ErrorInfo}"); } else if (repl_result.Value == 1) { string lastDate = repl_result.LastDate + ":" + repl_result.LastIndex + "-"; // 注意 - 符号不能少。少了意思就会变成每次只获取一条日志记录了 SaveStartDate(lastDate); } _lastReplicateTime = DateTime.Now; } } } // 检查升级 dp2ssl if (_updated == false // && StringUtil.IsDevelopMode() == false && ApplicationDeployment.IsNetworkDeployed == false && DateTime.Now - _lastUpdateTime > _updatePeriod) { WpfClientInfo.WriteInfoLog("开始自动检查升级"); // result.Value: // -1 出错 // 0 经过检查发现没有必要升级 // 1 成功 // 2 成功,但需要立即重新启动计算机才能让复制的文件生效 var update_result = await GreenInstaller.InstallFromWeb("http://dp2003.com/dp2ssl/v1_dev", "c:\\dp2ssl", "delayExtract,updateGreenSetupExe", //true, //true, token, null); if (update_result.Value == -1) { WpfClientInfo.WriteErrorLog($"自动检查升级出错: {update_result.ErrorInfo}"); } else { WpfClientInfo.WriteInfoLog($"结束自动检查升级 update_result:{update_result.ToString()}"); } if (update_result.Value == 1 || update_result.Value == 2) { App.TriggerUpdated("重启可使用新版本"); _updated = true; PageShelf.TrySetMessage(null, "dp2SSL 升级文件已经下载成功,下次重启时可自动升级到新版本"); } _lastUpdateTime = DateTime.Now; } } _monitorTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"监控专用线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("monitor", $"监控专用线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("监控专用线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
public static void WriteInfoLog(string text) { WpfClientInfo.WriteInfoLog(text); }
// parameters: // uid_table 返回 UID --> PII 对照表 public static NormalResult DownloadUidTable( List <string> item_dbnames, Hashtable uid_table, delegate_showText func_showProgress, // Delegate_writeLog writeLog, CancellationToken token) { WpfClientInfo.WriteInfoLog($"开始下载全部册记录到本地缓存"); LibraryChannel channel = App.CurrentApp.GetChannel(); var old_timeout = channel.Timeout; channel.Timeout = TimeSpan.FromMinutes(5); // 设置 5 分钟。因为册记录检索需要一定时间 try { if (item_dbnames == null) { long lRet = channel.GetSystemParameter( null, "item", "dbnames", out string strValue, out string strError); if (lRet == -1) { return new NormalResult { Value = -1, ErrorInfo = strError, ErrorCode = channel.ErrorCode.ToString() } } ; item_dbnames = StringUtil.SplitList(strValue); StringUtil.RemoveBlank(ref item_dbnames); } foreach (string dbName in item_dbnames) { func_showProgress?.Invoke($"正在从 {dbName} 获取信息 ..."); int nRedoCount = 0; REDO: if (token.IsCancellationRequested) { return new NormalResult { Value = -1, ErrorInfo = "用户中断" } } ; // 检索全部读者库记录 long lRet = channel.SearchItem(null, dbName, // "<all>", "", -1, "__id", "left", "zh", null, // strResultSetName "", // strSearchStyle "", // strOutputStyle out string strError); if (lRet == -1) { WpfClientInfo.WriteErrorLog($"SearchItem() 出错, strError={strError}, channel.ErrorCode={channel.ErrorCode}"); // 一次重试机会 if (lRet == -1 && (channel.ErrorCode == ErrorCode.RequestCanceled || channel.ErrorCode == ErrorCode.RequestError) && nRedoCount < 2) { nRedoCount++; goto REDO; } return(new NormalResult { Value = -1, ErrorInfo = strError, ErrorCode = channel.ErrorCode.ToString() }); } long hitcount = lRet; WpfClientInfo.WriteInfoLog($"{dbName} 共检索命中册记录 {hitcount} 条"); // 把超时时间改短一点 channel.Timeout = TimeSpan.FromSeconds(20); DateTime search_time = DateTime.Now; int skip_count = 0; int error_count = 0; if (hitcount > 0) { string strStyle = "id,cols,format:@coldef:*/barcode|*/location|*/uid"; // 获取和存储记录 ResultSetLoader loader = new ResultSetLoader(channel, null, null, strStyle, // $"id,xml,timestamp", "zh"); // loader.Prompt += this.Loader_Prompt; int i = 0; foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader) { if (token.IsCancellationRequested) { return new NormalResult { Value = -1, ErrorInfo = "用户中断" } } ; if (record.Cols != null) { string barcode = ""; if (record.Cols.Length > 0) { barcode = record.Cols[0]; } string location = ""; if (record.Cols.Length > 1) { location = record.Cols[1]; } string uid = ""; if (record.Cols.Length > 2) { uid = record.Cols[2]; } if (string.IsNullOrEmpty(barcode) == false && string.IsNullOrEmpty(uid) == false) { uid_table[uid] = barcode; } } i++; } } WpfClientInfo.WriteInfoLog($"dbName='{dbName}'。skip_count={skip_count}, error_count={error_count}"); } return(new NormalResult { Value = uid_table.Count, }); } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"DownloadItemRecordAsync() 出现异常:{ExceptionUtil.GetDebugText(ex)}"); return(new NormalResult { Value = -1, ErrorInfo = $"DownloadItemRecordAsync() 出现异常:{ex.Message}" }); } finally { channel.Timeout = old_timeout; App.CurrentApp.ReturnChannel(channel); WpfClientInfo.WriteInfoLog($"结束下载全部读者记录到本地缓存"); } }