// 从 dp2library 服务器获得全部 isbn 和 title 字符串 IsbnResult GetIsbnStrings() { LibraryChannel channel = _channelPool.GetChannel(this.textBox_dp2libraryUrl.Text, "public"); try { // TODO: <全部UNIMARC> long lRet = channel.SearchBiblio("<全部>", "", -1, "isbn", "left", "zh", "default", "", "", "", out string strQueryXml, out string strError); if (lRet == -1) { return new IsbnResult { Value = -1, ErrorInfo = strError } } ; ResultSetLoader loader = new ResultSetLoader(channel, "default", "id,xml"); IsbnResult result = new IsbnResult(); result.IsbnList = new List <string>(); foreach (Record record in loader) { // XML 转换为 MARC int nRet = MarcUtil.Xml2Marc(record.RecordBody.Xml, false, "", out string strMarcSyntax, out string strMARC, out strError); if (nRet != 0) { continue; } // 从 MARC 中取 010$a var marc_record = new MarcRecord(strMARC); string xpath = "field[@name='010']/subfield[@name='a']"; if (strMarcSyntax == "usmarc") { xpath = "field[@name='020']/subfield[@name='a']"; } var nodes = marc_record.select(xpath); foreach (MarcSubfield subfield in nodes) { result.IsbnList.Add(subfield.Content); } } return(result); } finally { _channelPool.ReturnChannel(channel); } }
// 初始化时列出当前馆藏地应有的全部图书 // 本函数中,只给 Entity 对象里面设置好了 PII,其他成员尚未设置 static void FillLocationBooks(EntityCollection entities, string location, CancellationToken token) { var channel = App.CurrentApp.GetChannel(); try { long lRet = channel.SearchItem(null, "<全部>", location, 5000, "馆藏地点", "exact", "zh", "shelfResultset", "", "", out string strError); if (lRet == -1) { throw new ChannelException(channel.ErrorCode, strError); } string strStyle = "id,cols,format:@coldef:*/barcode|*/borrower"; ResultSetLoader loader = new ResultSetLoader(channel, null, "shelfResultset", strStyle, "zh"); foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader) { token.ThrowIfCancellationRequested(); string pii = record.Cols[0]; Application.Current.Dispatcher.Invoke(new Action(() => { entities.Add(pii); })); } } finally { App.CurrentApp.ReturnChannel(channel); } }
// 第一阶段:获得全部读者库记录,进入本地数据库 // result.Value // -1 出错 // >=0 实际获得的读者记录条数 public static async Task <ReplicationPlan> DownloadAllPatronRecordAsync(CancellationToken token) { LibraryChannel channel = App.CurrentApp.GetChannel(); var old_timeout = channel.Timeout; channel.Timeout = TimeSpan.FromMinutes(5); // 设置 5 分钟。因为读者记录检索需要一定时间 try { ReplicationPlan plan = GetReplicationPlan(channel); if (plan.Value == -1) { return(plan); } int nRedoCount = 0; REDO: if (token.IsCancellationRequested) { return new ReplicationPlan { Value = -1, ErrorInfo = "用户中断" } } ; // 检索全部读者库记录 long lRet = channel.SearchReader(null, // stop, "<all>", "", -1, "__id", "left", "zh", null, // strResultSetName "", // strOutputStyle out string strError); if (lRet == -1) { // 一次重试机会 if (lRet == -1 && (channel.ErrorCode == ErrorCode.RequestCanceled || channel.ErrorCode == ErrorCode.RequestError) && nRedoCount < 2) { nRedoCount++; goto REDO; } return(new ReplicationPlan { Value = -1, ErrorInfo = strError, ErrorCode = channel.ErrorCode.ToString() }); } long hitcount = lRet; // 把超时时间改短一点 channel.Timeout = TimeSpan.FromSeconds(20); DateTime search_time = DateTime.Now; // 获取和存储记录 ResultSetLoader loader = new ResultSetLoader(channel, null, null, $"id,xml,timestamp", "zh"); using (BiblioCacheContext context = new BiblioCacheContext()) { context.Database.EnsureCreated(); // 删除 Patrons 里面的已有记录 context.Patrons.RemoveRange(context.Patrons.ToList()); await context.SaveChangesAsync(token); // loader.Prompt += this.Loader_Prompt; if (hitcount > 0) { int i = 0; foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader) { if (token.IsCancellationRequested) { return new ReplicationPlan { Value = -1, ErrorInfo = "用户中断" } } ; PatronItem item = new PatronItem(); var result = Set(item, record, search_time); if (result.Value == -1) { // TODO: 是否汇总报错信息? continue; } context.Patrons.Add(item); if ((i % 10) == 0) { await context.SaveChangesAsync(token); } i++; } await context.SaveChangesAsync(token); } } return(new ReplicationPlan { Value = (int)hitcount, StartDate = plan.StartDate }); } catch (Exception ex) { return(new ReplicationPlan { Value = -1, ErrorInfo = $"DownloadAllPatronRecord() 出现异常:{ex.Message}" }); } finally { channel.Timeout = old_timeout; App.CurrentApp.ReturnChannel(channel); } }
// 初始化一个读者库的指纹缓存 // return: // -1 出错 // >=0 实际发送给接口程序的事项数目 static int BuildOneDbCache( LibraryChannel channel, string strDir, string strReaderDbName, CancellationToken token, out string strError) { strError = ""; int nRet = 0; DpResultSet resultset = null; bool bCreate = false; Hashtable timestamp_table = new Hashtable(); // recpath --> fingerprint timestamp ShowMessage(strReaderDbName); // 结果集文件名 string strResultsetFilename = Path.Combine(strDir, strReaderDbName); if (File.Exists(strResultsetFilename) == false) { resultset = new DpResultSet(false, false); resultset.Create(strResultsetFilename, strResultsetFilename + ".index"); bCreate = true; } else { bCreate = false; } // *** 第一阶段, 创建新的结果集文件;或者获取全部读者记录中的指纹时间戳 bool bDone = false; // 创建情形下 是否完成了写入操作 try { /* * long lRet = Channel.SearchReader(stop, * strReaderDbName, * "1-9999999999", * -1, * "__id", * "left", * this.Lang, * null, // strResultSetName * "", // strOutputStyle * out strError); */ long lRet = channel.SearchReader(null, // stop, strReaderDbName, "", -1, "指纹时间戳", "left", ClientInfo.Lang, null, // strResultSetName "", // strOutputStyle out strError); if (lRet == -1) { if (channel.ErrorCode == ErrorCode.AccessDenied) { strError = "用户 " + channel.UserName + " 权限不足: " + strError; } return(-1); } if (lRet == 0) { // TODO: 这时候如果以前有结果集文件还会残留,但不会影响功能正确性,可以改进为把残留的结果集文件删除 return(0); } long lHitCount = lRet; ResultSetLoader loader = new ResultSetLoader(channel, null, null, bCreate == true ? "id,cols,format:cfgs/browse_fingerprint" : "id,cols,format:cfgs/browse_fingerprinttimestamp", ClientInfo.Lang); loader.Prompt += Loader_Prompt; foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader) { token.ThrowIfCancellationRequested(); ShowMessage("正在处理读者记录 " + record.Path); if (bCreate == true) { if (record.Cols == null || record.Cols.Length < 3) { strError = "record.Cols error ... 有可能是因为读者库缺乏配置文件 cfgs/browse_fingerprint"; return(-1); } if (string.IsNullOrEmpty(record.Cols[0]) == true) { continue; // 读者记录中没有指纹信息 } DpRecord item = new DpRecord(record.Path); // timestamp | barcode | fingerprint item.BrowseText = record.Cols[0] + "|" + record.Cols[1] + "|" + record.Cols[2]; resultset.Add(item); } else { if (record.Cols == null || record.Cols.Length < 1) { strError = "record.Cols error ... 有可能是因为读者库缺乏配置文件 cfgs/browse_fingerprinttimestamp"; return(-1); } if (record.Cols.Length < 2) { strError = "record.Cols error ... 需要刷新配置文件 cfgs/browse_fingerprinttimestamp 到最新版本"; return(-1); } if (string.IsNullOrEmpty(record.Cols[0]) == true) { continue; // 读者记录中没有指纹信息 } // 记载时间戳 // timestamp | barcode timestamp_table[record.Path] = record.Cols[0] + "|" + record.Cols[1]; } } #if NO // stop.SetProgressRange(0, lHitCount); long lStart = 0; long lCount = lHitCount; DigitalPlatform.LibraryClient.localhost.Record[] searchresults = null; // 装入浏览格式 for (; ;) { token.ThrowIfCancellationRequested(); lRet = channel.GetSearchResult( null, null, // strResultSetName lStart, lCount, bCreate == true ? "id,cols,format:cfgs/browse_fingerprint" : "id,cols,format:cfgs/browse_fingerprinttimestamp", ClientInfo.Lang, out searchresults, out strError); if (lRet == -1) { return(-1); } if (lRet == 0) { strError = "GetSearchResult() return 0"; return(-1); } Debug.Assert(searchresults != null, ""); Debug.Assert(searchresults.Length > 0, ""); for (int i = 0; i < searchresults.Length; i++) { DigitalPlatform.LibraryClient.localhost.Record record = searchresults[i]; if (bCreate == true) { if (record.Cols == null || record.Cols.Length < 3) { strError = "record.Cols error ... 有可能是因为读者库缺乏配置文件 cfgs/browse_fingerprint"; return(-1); } if (string.IsNullOrEmpty(record.Cols[0]) == true) { continue; // 读者记录中没有指纹信息 } DpRecord item = new DpRecord(record.Path); // timestamp | barcode | fingerprint item.BrowseText = record.Cols[0] + "|" + record.Cols[1] + "|" + record.Cols[2]; resultset.Add(item); } else { if (record.Cols == null || record.Cols.Length < 1) { strError = "record.Cols error ... 有可能是因为读者库缺乏配置文件 cfgs/browse_fingerprinttimestamp"; return(-1); } if (record.Cols.Length < 2) { strError = "record.Cols error ... 需要刷新配置文件 cfgs/browse_fingerprinttimestamp 到最新版本"; return(-1); } if (string.IsNullOrEmpty(record.Cols[0]) == true) { continue; // 读者记录中没有指纹信息 } // 记载时间戳 // timestamp | barcode timestamp_table[record.Path] = record.Cols[0] + "|" + record.Cols[1]; } } lStart += searchresults.Length; lCount -= searchresults.Length; //stop.SetMessage(strReaderDbName + " 包含记录 " + lHitCount.ToString() + " 条,已装入 " + lStart.ToString() + " 条"); if (lStart >= lHitCount || lCount <= 0) { break; } //stop.SetProgressValue(lStart); } #endif if (bCreate == true) { bDone = true; } if (bCreate == true) { // return: // -2 remoting服务器连接失败。驱动程序尚未启动 // -1 出错 // >=0 实际发送给接口程序的事项数目 nRet = CreateFingerprintCache(resultset, out strError); if (nRet == -1 || nRet == -2) { return(-1); } return(nRet); } } finally { if (bCreate == true) { Debug.Assert(resultset != null, ""); if (bDone == true) { resultset.Detach(out string strTemp1, out string strTemp2); } else { // 否则文件会被删除 resultset.Close(); } } } // 比对时间戳,更新结果集文件 Hashtable update_table = new Hashtable(); // 需要更新的事项。recpath --> 1 resultset = new DpResultSet(false, false); resultset.Attach(strResultsetFilename, strResultsetFilename + ".index"); try { long nCount = resultset.Count; for (long i = 0; i < nCount; i++) { token.ThrowIfCancellationRequested(); DpRecord record = resultset[i]; string strRecPath = record.ID; ShowMessage("比对 " + strRecPath); // timestamp | barcode string strNewTimestamp = (string)timestamp_table[strRecPath]; if (strNewTimestamp == null) { // 最新状态下,读者记录已经不存在,需要从结果集中删除 resultset.RemoveAt((int)i); i--; nCount--; continue; } // 拆分出证条码号 2013/1/28 string strNewBarcode = ""; nRet = strNewTimestamp.IndexOf("|"); if (nRet != -1) { strNewBarcode = strNewTimestamp.Substring(nRet + 1); strNewTimestamp = strNewTimestamp.Substring(0, nRet); } // 最新读者记录中已经没有指纹信息。例如读者记录中的指纹元素被删除了 if (string.IsNullOrEmpty(strNewTimestamp) == true) { // 删除现有事项 resultset.RemoveAt((int)i); i--; nCount--; timestamp_table.Remove(strRecPath); continue; } // 取得结果集文件中的原有时间戳字符串 string strText = record.BrowseText; // timestamp | barcode | fingerprint nRet = strText.IndexOf("|"); if (nRet == -1) { strError = "browsetext 错误,没有 '|' 字符"; return(-1); } string strOldTimestamp = strText.Substring(0, nRet); // timestamp | barcode | fingerprint string strOldBarcode = strText.Substring(nRet + 1); nRet = strOldBarcode.IndexOf("|"); if (nRet != -1) { strOldBarcode = strOldBarcode.Substring(0, nRet); } // 时间戳发生变化,需要更新事项 if (strNewTimestamp != strOldTimestamp || strNewBarcode != strOldBarcode) { // 如果证条码号为空,无法建立对照关系,要跳过 if (string.IsNullOrEmpty(strNewBarcode) == false) { update_table[strRecPath] = 1; } // 删除现有事项 resultset.RemoveAt((int)i); i--; nCount--; } timestamp_table.Remove(strRecPath); } // 循环结束后,timestamp_table中剩余的是当前结果集文件中没有包含的那些读者记录路径 if (update_table.Count > 0) { // 获取指纹信息,追加到结果集文件的尾部 // parameters: // update_table key为读者记录路径 AppendFingerprintInfo( channel, resultset, update_table, token); } // 如果服务器端新增了指纹信息,需要获取后追加到结果集文件尾部 if (timestamp_table.Count > 0) { // 获取指纹信息,追加到结果集文件的尾部 // parameters: // update_table key为读者记录路径 AppendFingerprintInfo( channel, resultset, timestamp_table, token); } // return: // -2 remoting服务器连接失败。驱动程序尚未启动 // -1 出错 // >=0 实际发送给接口程序的事项数目 nRet = CreateFingerprintCache(resultset, out strError); if (nRet == -1 || nRet == -2) { return(-1); } return(nRet); } finally { resultset.Detach(out string strTemp1, out string strTemp2); } }
// 第一阶段:获得全部读者库记录,进入本地数据库 // result.Value // -1 出错 // >=0 实际获得的读者记录条数 public static async Task <ReplicationPlan> DownloadAllPatronRecordAsync( Delegate_writeLog writeLog, CancellationToken token) { _inDownloadingPatron++; // 2020/9/26 if (_inDownloadingPatron > 1) { _inDownloadingPatron--; return(new ReplicationPlan { Value = -1, ErrorCode = "running", ErrorInfo = "前一次的“下载全部读者记录到本地缓存”过程还在进行中,本次触发被放弃" }); } writeLog?.Invoke($"开始下载全部读者记录到本地缓存"); LibraryChannel channel = App.CurrentApp.GetChannel(); var old_timeout = channel.Timeout; channel.Timeout = TimeSpan.FromMinutes(5); // 设置 5 分钟。因为读者记录检索需要一定时间 try { ReplicationPlan plan = GetReplicationPlan(channel); writeLog?.Invoke($"GetReplicationPlan() return {plan.ToString()}"); if (plan.Value == -1) { return(plan); } int nRedoCount = 0; REDO: if (token.IsCancellationRequested) { return new ReplicationPlan { Value = -1, ErrorInfo = "用户中断" } } ; // 检索全部读者库记录 long lRet = channel.SearchReader(null, // stop, "<all>", "", -1, "__id", "left", "zh", null, // strResultSetName "", // strOutputStyle out string strError); if (lRet == -1) { writeLog?.Invoke($"SearchReader() 出错, strError={strError}, channel.ErrorCode={channel.ErrorCode}"); // 一次重试机会 if (lRet == -1 && (channel.ErrorCode == ErrorCode.RequestCanceled || channel.ErrorCode == ErrorCode.RequestError) && nRedoCount < 2) { nRedoCount++; goto REDO; } return(new ReplicationPlan { Value = -1, ErrorInfo = strError, ErrorCode = channel.ErrorCode.ToString() }); } long hitcount = lRet; writeLog?.Invoke($"共检索命中读者记录 {hitcount} 条"); // 把超时时间改短一点 channel.Timeout = TimeSpan.FromSeconds(20); DateTime search_time = DateTime.Now; Hashtable pii_table = new Hashtable(); int skip_count = 0; int error_count = 0; // 获取和存储记录 ResultSetLoader loader = new ResultSetLoader(channel, null, null, $"id,xml,timestamp", "zh"); using (BiblioCacheContext context = new BiblioCacheContext()) { context.Database.EnsureCreated(); // 删除 Patrons 里面的已有记录 context.Patrons.RemoveRange(context.Patrons.ToList()); await context.SaveChangesAsync(token); // loader.Prompt += this.Loader_Prompt; if (hitcount > 0) { int i = 0; foreach (DigitalPlatform.LibraryClient.localhost.Record record in loader) { if (token.IsCancellationRequested) { return new ReplicationPlan { Value = -1, ErrorInfo = "用户中断" } } ; PatronItem item = new PatronItem(); // result.Value: // -1 出错 // 0 需要跳过这条读者记录 // 1 成功 var result = Set(item, record, search_time); if (result.Value == -1 || result.Value == 0) { // TODO: 是否汇总报错信息? if (result.Value == -1) { writeLog?.Invoke($"Set() ({item.RecPath}) 出错: {result.ErrorInfo}"); error_count++; } if (result.Value == 0) { skip_count++; } continue; } // if (pii_table.ContainsKey(result.PII)) { string recpath = (string)pii_table[result.PII]; writeLog?.Invoke($"发现读者记录 {item.RecPath} 的 PII '{result.PII}' 和 {recpath} 的 PII 重复了。跳过它"); continue; } pii_table[result.PII] = item.RecPath; // TODO: PII 应该是包含 OI 的严格形态 context.Patrons.Add(item); if ((i % 10) == 0) { await context.SaveChangesAsync(token); } i++; } await context.SaveChangesAsync(token); } } writeLog?.Invoke($"plan.StartDate='{plan.StartDate}'。skip_count={skip_count}, error_count={error_count}。返回"); return(new ReplicationPlan { Value = (int)hitcount, StartDate = plan.StartDate }); } catch (Exception ex) { // 2020/9/26 writeLog?.Invoke($"DownloadAllPatronRecord() 出现异常:{ExceptionUtil.GetDebugText(ex)}"); return(new ReplicationPlan { Value = -1, ErrorInfo = $"DownloadAllPatronRecord() 出现异常:{ex.Message}" }); } finally { channel.Timeout = old_timeout; App.CurrentApp.ReturnChannel(channel); writeLog?.Invoke($"结束下载全部读者记录到本地缓存"); _inDownloadingPatron--; } }
private static void Zserver_PresentGetRecords(object sender, PresentGetRecordsEventArgs e) { string strError = ""; int nCondition = 100; // (unspecified)error ZServerChannel zserver_channel = (ZServerChannel)sender; if (zserver_channel == null) { throw new ArgumentException("zserver_channel 为空"); } string strInstanceName = zserver_channel.EnsureProperty().GetKeyValue("i_n"); if (strInstanceName == null) { strError = "通道中 实例名 'i_n' 尚未初始化"; // ?? bug LibraryManager.Log?.Error(strError); goto ERROR1; } Instance instance = FindZ3950Instance(strInstanceName, out strError); if (instance == null) { if (string.IsNullOrEmpty(strError)) { strError = "实例名 '" + strInstanceName + "' 不存在(或实例没有启用 Z39.50 服务)"; } goto ERROR1; } if (instance.Running == false) { strError = "实例 '" + instance.Name + "' 正在维护中,暂时不能访问"; nCondition = 1019; // Init/AC: System not available due to maintenance goto ERROR1; } string strResultSetName = e.Request.m_strResultSetID; if (String.IsNullOrEmpty(strResultSetName) == true) { strResultSetName = "default"; } long lStart = e.Request.m_lResultSetStartPoint - 1; long lNumber = e.Request.m_lNumberOfRecordsRequested; int MAX_PRESENT_RECORD = 100; // 限制每次 present 的记录数量 if (lNumber > MAX_PRESENT_RECORD) { lNumber = MAX_PRESENT_RECORD; } DiagFormat diag = null; List <RetrivalRecord> records = new List <RetrivalRecord>(); string strUserName = zserver_channel.EnsureProperty().GetKeyValue("i_u"); string strPassword = zserver_channel.EnsureProperty().GetKeyValue("i_p"); LoginInfo login_info = BuildLoginInfo(strUserName, strPassword); LibraryChannel library_channel = instance.MessageConnection.GetChannel(login_info); zserver_channel.Tag = library_channel; try { // 全局结果集名 string resultset_name = MakeGlobalResultSetName(zserver_channel, strResultSetName); // TODO: timestamp 有必要获取么? ResultSetLoader loader = new ResultSetLoader(library_channel, resultset_name, "id,xml,timestamp") { Start = lStart, BatchSize = Math.Min(10, lNumber) }; int i = 0; int nSize = 0; foreach (DigitalPlatform.LibraryClient.localhost.Record dp2library_record in loader) { if (i >= lNumber) { break; } // 判断请求边界是否合法。放到循环里面的 i==0 时刻来做,是因为枚举器(loader)需要请求 dp2library 之后才能知道结果集大小 if (i == 0) { e.TotalCount = loader.TotalCount; if (lStart >= loader.TotalCount) { DiagFormat diag1 = null; ZProcessor.SetPresentDiagRecord(ref diag1, 13, // Present request out-of-range "Present 所请求的起始偏移位置 " + lStart + " 超过结果集中记录总数 " + loader.TotalCount); e.Diag = diag1; return; } if (lStart + lNumber > loader.TotalCount) { DiagFormat diag1 = null; ZProcessor.SetPresentDiagRecord(ref diag1, 13, // Present request out-of-range "Present 所请求的结束偏移位置 " + (lStart + lNumber) + " 超过结果集中记录总数 " + loader.TotalCount); e.Diag = diag1; return; } } { // 解析出数据库名和ID string strDbName = dp2StringUtil.GetDbName(dp2library_record.Path); string strRecID = dp2StringUtil.GetRecordID(dp2library_record.Path); // 如果取得的是xml记录,则根元素可以看出记录的marc syntax,进一步可以获得oid; // 如果取得的是MARC格式记录,则需要根据数据库预定义的marc syntax来看出oid了 // string strMarcSyntaxOID = GetMarcSyntaxOID(instance, strDbName); // string strMarcSyntaxOID = GetMarcSyntaxOID(dp2library_record); RetrivalRecord record = new RetrivalRecord { m_strDatabaseName = strDbName }; // 根据书目库名获得书目库属性对象 // TODO: 这里可以考虑 cache BiblioDbProperty prop = instance.zhost.GetDbProperty( strDbName, false); int nRet = GetIso2709Record(dp2library_record, e.Request.m_elementSetNames, prop != null ? prop.AddField901 : false, prop != null ? prop.RemoveFields : "997", zserver_channel.EnsureProperty().MarcRecordEncoding, out string strMarcSyntaxOID, out byte[] baIso2709, out strError); /* * // 测试记录群中包含诊断记录 * if (i == 1) * { * nRet = -1; * strError = "测试获取记录错误"; * } */ if (nRet == -1) { record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 14, // system error in presenting records m_strAddInfo = strError }; } else if (nRet == 0) { record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 1028, // record deleted m_strAddInfo = strError }; } else if (String.IsNullOrEmpty(strMarcSyntaxOID) == true) { // 根据数据库名无法获得marc syntax oid。可能是虚拟库检索命中记录所在的物理库没有在 capo.xml 中配置。 record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 227, // No data available in requested record syntax // 239? // m_strAddInfo = "根据数据库名 '" + strDbName + "' 无法获得 marc syntax oid" m_strAddInfo = "根据书目记录 '" + dp2library_record.Path + "' 的 MARCXML 无法获得 marc syntax oid" }; } else { record.m_external = new External { m_strDirectRefenerce = strMarcSyntaxOID, m_octectAligned = baIso2709 }; } // TODO: 测试观察这里的 size 增长情况 nSize += record.GetPackageSize(); if (i == 0) { // 连一条记录也放不下 if (nSize > zserver_channel.EnsureProperty().ExceptionalRecordSize) { Debug.Assert(diag == null, ""); ZProcessor.SetPresentDiagRecord(ref diag, 17, // record exceeds Exceptional_record_size "记录尺寸 " + nSize.ToString() + " 超过 Exceptional_record_size " + zserver_channel.EnsureProperty().ExceptionalRecordSize.ToString()); lNumber = 0; break; } } else { if (nSize >= zserver_channel.EnsureProperty().PreferredMessageSize) { // TODO: 记入日志? // 调整返回的记录数 lNumber = i; break; } } records.Add(record); } i++; // 2018/9/28 // 防范性编程 // TODO: 还可以通过时间长度来控制。超过一定时间,就抛出异常 if (i > MAX_PRESENT_RECORD) { throw new Exception("Zserver_PresentGetRecords() 中获取记录的循环超过极限数量 " + MAX_PRESENT_RECORD + "(此时 lNumber=" + lNumber + ")"); } } } catch (ChannelException ex) { // 指定的结果集没有找到 if (ex.ErrorCode == DigitalPlatform.LibraryClient.localhost.ErrorCode.NotFound) { nCondition = 30; // Specified result set does not exist strError = ex.Message; goto ERROR1; } strError = "获取结果集时出现异常(ChannelException): " + ex.Message; goto ERROR1; } catch (Exception ex) { strError = "获取结果集时出现异常: " + ex.Message; goto ERROR1; } finally { zserver_channel.Tag = null; instance.MessageConnection.ReturnChannel(library_channel); } e.Records = records; e.Diag = diag; return; ERROR1: { DiagFormat diag1 = null; ZProcessor.SetPresentDiagRecord(ref diag1, nCondition, strError); e.Diag = diag1; return; } }
private static void Zserver_PresentGetRecords(object sender, PresentGetRecordsEventArgs e) { string strError = ""; ZServerChannel zserver_channel = (ZServerChannel)sender; string strInstanceName = zserver_channel.SetProperty().GetKeyValue("i_n"); if (strInstanceName == null) { strError = "通道中 实例名 '" + strInstanceName + "' 尚未初始化"; ZManager.Log.Error(strError); goto ERROR1; } Instance instance = FindInstance(strInstanceName); if (instance == null) { strError = "实例名 '" + strInstanceName + "' 不存在"; goto ERROR1; } string strResultSetName = e.Request.m_strResultSetID; if (String.IsNullOrEmpty(strResultSetName) == true) { strResultSetName = "default"; } long lStart = e.Request.m_lResultSetStartPoint - 1; long lNumber = e.Request.m_lNumberOfRecordsRequested; int MAX_PRESENT_RECORD = 100; // 限制每次 present 的记录数量 if (lNumber > MAX_PRESENT_RECORD) { lNumber = MAX_PRESENT_RECORD; } DiagFormat diag = null; List <RetrivalRecord> records = new List <RetrivalRecord>(); string strUserName = zserver_channel.SetProperty().GetKeyValue("i_u"); string strPassword = zserver_channel.SetProperty().GetKeyValue("i_p"); LoginInfo login_info = new LoginInfo { UserName = strUserName, Password = strPassword }; LibraryChannel library_channel = instance.MessageConnection.GetChannel(login_info); try { // 全局结果集名 string resultset_name = MakeGlobalResultSetName(zserver_channel, strResultSetName); ResultSetLoader loader = new ResultSetLoader(library_channel, resultset_name, "id,xml,timestamp") { Start = lStart, BatchSize = Math.Min(10, lNumber) }; int i = 0; int nSize = 0; foreach (DigitalPlatform.LibraryClient.localhost.Record dp2library_record in loader) { if (i >= lNumber) { break; } if (i == 0) { e.TotalCount = loader.TotalCount; if (lStart >= loader.TotalCount) { DiagFormat diag1 = null; ZProcessor.SetPresentDiagRecord(ref diag1, 13, // Present request out-of-range "Present 所请求的起始偏移位置 " + lStart + " 超过结果集中记录总数 " + loader.TotalCount); e.Diag = diag1; return; } } { // 解析出数据库名和ID string strDbName = dp2StringUtil.GetDbName(dp2library_record.Path); string strRecID = dp2StringUtil.GetRecordID(dp2library_record.Path); // 如果取得的是xml记录,则根元素可以看出记录的marc syntax,进一步可以获得oid; // 如果取得的是MARC格式记录,则需要根据数据库预定义的marc syntax来看出oid了 string strMarcSyntaxOID = GetMarcSyntaxOID(instance, strDbName); RetrivalRecord record = new RetrivalRecord { m_strDatabaseName = strDbName }; // 根据书目库名获得书目库属性对象 BiblioDbProperty prop = instance.zhost.GetDbProperty( strDbName, false); int nRet = GetIso2709Record(dp2library_record, e.Request.m_elementSetNames, prop != null ? prop.AddField901 : false, prop != null ? prop.RemoveFields : "997", zserver_channel.SetProperty().MarcRecordEncoding, out byte[] baIso2709, out strError); /* * // 测试记录群中包含诊断记录 * if (i == 1) * { * nRet = -1; * strError = "测试获取记录错误"; * } */ if (nRet == -1) { record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 14, // system error in presenting records m_strAddInfo = strError }; } else if (nRet == 0) { record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 1028, // record deleted m_strAddInfo = strError }; } else if (String.IsNullOrEmpty(strMarcSyntaxOID) == true) { // 根据数据库名无法获得marc syntax oid。可能是虚拟库检索命中记录所在的物理库没有在 capo.xml 中配置。 record.m_surrogateDiagnostic = new DiagFormat { m_strDiagSetID = "1.2.840.10003.4.1", m_nDiagCondition = 109, // database unavailable // 似乎235:database dos not exist也可以 m_strAddInfo = "根据数据库名 '" + strDbName + "' 无法获得 marc syntax oid" }; } else { record.m_external = new External { m_strDirectRefenerce = strMarcSyntaxOID, m_octectAligned = baIso2709 }; } nSize += record.GetPackageSize(); if (i == 0) { // 连一条记录也放不下 if (nSize > zserver_channel.SetProperty().ExceptionalRecordSize) { Debug.Assert(diag == null, ""); ZProcessor.SetPresentDiagRecord(ref diag, 17, // record exceeds Exceptional_record_size "记录尺寸 " + nSize.ToString() + " 超过 Exceptional_record_size " + zserver_channel.SetProperty().ExceptionalRecordSize.ToString()); lNumber = 0; break; } } else { if (nSize >= zserver_channel.SetProperty().PreferredMessageSize) { // 调整返回的记录数 lNumber = i; break; } } records.Add(record); } i++; } } catch (Exception ex) { strError = "获取结果集时出现异常: " + ex.Message; goto ERROR1; } finally { instance.MessageConnection.ReturnChannel(library_channel); } e.Records = records; e.Diag = diag; return; ERROR1: { DiagFormat diag1 = null; ZProcessor.SetPresentDiagRecord(ref diag1, 2, // temporary system error strError); e.Diag = diag1; // e.Result = new ZClient.SearchResult { Value = -1, ErrorInfo = strError }; return; } }
// 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($"结束下载全部读者记录到本地缓存"); } }