dsh-d365-kit 0.34.1 → 0.34.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +318 -308
- package/CHANGELOG.zh-CN.md +317 -307
- package/INSTALL.md +94 -94
- package/INSTALL.zh-CN.md +93 -93
- package/LICENSE +21 -21
- package/README.md +336 -327
- package/README.zh-CN.md +336 -327
- package/client.js +1818 -1818
- package/cordis.patch.yml +44 -44
- package/docs/en/architecture.md +172 -172
- package/docs/en/checks.md +184 -184
- package/docs/en/installation.md +151 -151
- package/docs/en/permissions.md +161 -161
- package/docs/en/scenarios.md +202 -202
- package/docs/en/tools.md +665 -665
- package/docs/en/troubleshooting.md +131 -131
- package/docs/zh/architecture.md +172 -172
- package/docs/zh/checks.md +184 -184
- package/docs/zh/installation.md +151 -151
- package/docs/zh/permissions.md +161 -161
- package/docs/zh/scenarios.md +202 -202
- package/docs/zh/tools.md +665 -665
- package/docs/zh/troubleshooting.md +131 -131
- package/examples/prompts.en.md +206 -206
- package/examples/prompts.zh-CN.md +206 -206
- package/examples/waivers.example.json +20 -20
- package/icon.svg +6 -6
- package/index.js +139 -139
- package/lib/auth.js +181 -181
- package/lib/client.js +496 -496
- package/lib/code-check.js +549 -549
- package/lib/config.js +323 -323
- package/lib/contract-history.js +49 -49
- package/lib/contract.js +583 -583
- package/lib/csharp-check.js +493 -493
- package/lib/data-migrate.js +1318 -1318
- package/lib/data-policy.js +78 -78
- package/lib/error-codes.js +80 -80
- package/lib/gitlab.js +161 -161
- package/lib/health.js +205 -205
- package/lib/migrate.js +732 -732
- package/lib/preflight.js +116 -116
- package/lib/refs.js +191 -191
- package/lib/resources.js +320 -320
- package/lib/retention-defaults.js +17 -17
- package/lib/retention.js +384 -384
- package/lib/runtime.js +232 -232
- package/lib/settings-http.js +512 -512
- package/lib/settings-store.js +371 -371
- package/lib/snapshot.js +149 -149
- package/lib/tool-code-check.js +133 -133
- package/lib/tool-configure.js +169 -169
- package/lib/tool-contract.js +376 -376
- package/lib/tool-data-sync.js +559 -559
- package/lib/tool-data-write.js +203 -203
- package/lib/tool-data.js +282 -282
- package/lib/tool-diff.js +484 -484
- package/lib/tool-environments.js +106 -106
- package/lib/tool-flows.js +540 -540
- package/lib/tool-health.js +127 -127
- package/lib/tool-layers.js +497 -497
- package/lib/tool-metadata.js +417 -417
- package/lib/tool-migrate.js +102 -102
- package/lib/tool-refs.js +206 -206
- package/lib/tool-reports.js +299 -299
- package/lib/tool-sync.js +430 -430
- package/lib/tool-write.js +563 -563
- package/lib/waivers.js +83 -83
- package/lib/write-actions.js +1374 -1374
- package/lib/write-policy.js +199 -199
- package/lib/write-store.js +167 -167
- package/locale/en.json +68 -68
- package/locale/zh.json +68 -68
- package/package.json +2 -2
- package/skills/d365-troubleshooting/SKILL.md +115 -115
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: d365-troubleshooting
|
|
3
|
-
description: 用 dsh-d365-kit 排查 Dynamics 365 / Dataverse 插件失败:插件报错、跟踪日志、步骤注册、消息复现、异步作业。Use when a D365 plug-in, custom action, flow or async job fails and you need to find the cause with the d365_* tools.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 排查 D365 插件失败
|
|
7
|
-
|
|
8
|
-
## 先用对工具(按问题查)
|
|
9
|
-
|
|
10
|
-
| 你手上是这个问题 | 用这个 | 不要用 |
|
|
11
|
-
| --- | --- | --- |
|
|
12
|
-
| 这个环境里到底有什么、版本多少 | `d365_envs`(`check` 真连) | 猜 URL |
|
|
13
|
-
| 这张表有哪些字段、某个字段的类型/默认值 | `d365_metadata resource=attributes`(派生列先 `cast`) | `d365_data` |
|
|
14
|
-
| 这个字段为什么表单上没有 / 改了不生效 | `d365_refs kind=field` + `d365_layers` | 只看 `attributes` |
|
|
15
|
-
| 两个环境差在哪 | `d365_sync` 两边 → `d365_diff` | 两边各查一遍对眼 |
|
|
16
|
-
| 目标环境缺哪个程序集/类型/步骤 | `d365_contract drift` | 翻 `plugin_steps` 5000 行 |
|
|
17
|
-
| 这个表/选项集被哪些解决方案盖过 | `d365_layers` | `d365_metadata solutions` |
|
|
18
|
-
| 导入会不会覆盖别人的层 | `d365_migrate` | `d365_diff` |
|
|
19
|
-
| 把一批记录复制到另一个环境 | `d365_data_sync plan` → `execute` | 用 `d365_data_write` 手抄 |
|
|
20
|
-
| 改一批已存在记录的某个字段 | `d365_data_sync plan-update` → `execute` | `d365_data_write` 一条一张卡 |
|
|
21
|
-
| 改/建一条记录 | `d365_data_write` | `d365_write` |
|
|
22
|
-
| 改元数据(字段、显示名、选项值、移除自定义层) | `d365_write`(`plan-*` 只读,`execute` 确认) | 手写 Web API |
|
|
23
|
-
| 这条记录长什么样、有多少条 | `d365_data`(`count`/`group`/`sample`/`query`) | `d365_metadata` |
|
|
24
|
-
| 插件报错,为什么 | `d365_trace summary` → `get`,先 `d365_health` 排除异步作业 | 猜错误码 |
|
|
25
|
-
| 这条消息现在返回什么 | `d365_invoke` | Postman 手拼 |
|
|
26
|
-
| 这条表的逻辑是不是在云流里 / 哪些 flow 会碰它 | `d365_flows refs` → `show` | 在 Power Automate 门户里逐条点开 |
|
|
27
|
-
| 产物太大看不全 | `d365_reports action=view`(`match` 找 GUID) | 全量读文件 |
|
|
28
|
-
|
|
29
|
-
## 什么时候用
|
|
30
|
-
|
|
31
|
-
插件抛异常、自定义消息返回 400/500、某个表上的同步逻辑突然不动、异步作业积压——任何「元数据看起来对,但运行时不对」的问题。目标不是读一堆日志,而是**尽快拿到一个签名 + 一个可复现的调用**。
|
|
32
|
-
|
|
33
|
-
## 先确认三件事(30 秒,别跳)
|
|
34
|
-
|
|
35
|
-
1. **哪个环境**:`d365_envs` 看配置与连通性。环境搞错是最常见的假故障。
|
|
36
|
-
2. **权限够不够**:跟踪日志属于业务数据,要 `data.read`;调 POST 消息要 `message.invoke`(默认关)。
|
|
37
|
-
3. **跟踪有没有开**:`d365_health` 的「插件跟踪」列 = 关闭 / 仅异常 / 全部。列显示「关闭」时,任何 trace 查询都查不到东西——先让环境开起来,再谈排查。
|
|
38
|
-
|
|
39
|
-
## 排查顺序
|
|
40
|
-
|
|
41
|
-
### 1. 先看是不是异步作业,而不是插件
|
|
42
|
-
|
|
43
|
-
```json
|
|
44
|
-
{ "environments": ["sit"], "hours": 48 }
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
`d365_health` 的挂起 / 近期失败(Completed 且 Failed)指向 `asyncoperation`。异步作业失败**不会**写插件跟踪日志,这两条线不要混。
|
|
48
|
-
|
|
49
|
-
### 2. 归并签名:几百条失败通常是同一个 bug
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{ "action": "summary", "hours": 24, "entity": "contact" }
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
`d365_trace summary` 按「插件类型 + 消息 + 表 + 异常首行」归并。签名一样 = 同一个 bug 在反复发生,接着只查一条就够。
|
|
56
|
-
|
|
57
|
-
### 3. 取一条完整调用
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{ "action": "list", "typename": "ContactSearch", "hours": 24, "top": 20 }
|
|
61
|
-
{ "action": "get", "traceId": "<上一步的 id>" }
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
`get` 给异常全文 + messageblock(入参)。messageblock 里有 `secureConfig` 的明文密钥,工具会屏蔽成 `***`——**不要**为了「看得清楚」去开 `includeConfig`。
|
|
65
|
-
|
|
66
|
-
### 4. 步骤到底注册在哪
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
{ "resource": "plugin_steps", "top": 1000, "filter": "contains(typename,'ContactSearch')" }
|
|
70
|
-
{ "resource": "step_images", "top": 200 }
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
看三样:`message`(Create / Update / RetrieveMultiple)、`stage`(20 前 / 40 后)、`mode`(同步 0 / 异步 1),以及 `filteringattributes`。步骤存在但没触发,九成是这三项之一不对,而不是代码不对。
|
|
74
|
-
|
|
75
|
-
### 5. 复现那条消息
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{ "message": "contoso_Recalculate", "method": "POST", "entity": "accounts",
|
|
79
|
-
"id": "<guid>", "parameters": { "Target": { "@odata.type": "Microsoft.Dynamics.CRM.account" } } }
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
`d365_invoke` **失败不抛异常**,会把 HTTP 状态、Dataverse 错误码和响应体原样给你——这正是插件抛出的原始形状。GET function 走 `data.read`,POST action 走 `message.invoke`,每次都会弹确认卡片。
|
|
83
|
-
|
|
84
|
-
### 6. 需要时再往下挖
|
|
85
|
-
|
|
86
|
-
- 数据是否符合预期:`d365_data { "action": "query", "entity": "contact", "filter": "..." }`
|
|
87
|
-
- 谁引用了这个字段/脚本/程序集:`d365_refs`
|
|
88
|
-
- 一个环境有、另一个环境没有:`d365_contract`(插件步骤/程序集版本漂移)、`d365_layers`(谁盖住了谁)
|
|
89
|
-
- 元数据本身对不上:`d365_metadata resource=attributes ... cast=...`(`DefaultValue`、`OptionSet`、`Targets` 必须先 cast)
|
|
90
|
-
- 逻辑不在插件里、而是被云流接走了:`d365_flows refs entity=<表>` 看哪些 flow 会碰这张表,再 `show` 看它的触发器和步骤(云流的运行历史不在 Dataverse,失败要看 Power Automate 门户)
|
|
91
|
-
|
|
92
|
-
## 错误码速查
|
|
93
|
-
|
|
94
|
-
| 错误码 | 通常意味着 | 下一步 |
|
|
95
|
-
| --- | --- | --- |
|
|
96
|
-
| `0x80048d19` | 请求体形状不对:把 lookup 写成了裸 GUID,而不是 `字段@odata.bind`(而且键要用导航属性名) | 查 `RelationshipDefinitions` 的 `ReferencingEntityNavigationPropertyName` |
|
|
97
|
-
| `0x80040216` / `0x80040217` | 引用的记录不存在(常见于跨环境复制后 GUID 不匹配) | `d365_data query id=...` 确认;迁移用 `plan` 的 lookup 报告 |
|
|
98
|
-
| `0x80040333` | 重复检测规则拦下 | 确认是不是真重复;批量写时再考虑 `MSCRM.SuppressDuplicateDetection` |
|
|
99
|
-
| `0x80044150` | 并发冲突(别人同时改了同一条) | 重试单条,不要盲目放大并发 |
|
|
100
|
-
| `0x80048423` | 受管组件不可改 | 在未受管解决方案里改,别碰受管层 |
|
|
101
|
-
| `0x8004a0f6` | 状态机不允许这次状态转换 | 走对应消息(如 `CloseIncident`),不要直接改 statuscode |
|
|
102
|
-
| `0x80060888` | 属性名或查询参数不被这个端点支持 | 元数据端点大多不支持 `$filter`/`$orderby`/`$top`;派生列要 cast |
|
|
103
|
-
|
|
104
|
-
## 三个真实踩过的坑
|
|
105
|
-
|
|
106
|
-
- **成功执行的 trace 存空串,不是 null**。只写 `exceptiondetails ne null` 会把成功执行也列成失败——至少多算了 3 倍。`d365_trace` 默认已经排除。
|
|
107
|
-
- **`$count` 在 5000 截断**,`d365_metadata` 显示「≥5000」不是 bug,是服务端上限。要精确总数就分组或加条件。
|
|
108
|
-
- **元数据读和业务数据读是两道闸**:`metadata.read` 默认开,`data.read` 默认关。查 trace 被拒时看的是 `data.read`,不是元数据那一道。
|
|
109
|
-
|
|
110
|
-
## 不要做的事
|
|
111
|
-
|
|
112
|
-
- 不要连生产环境试 `execute`/`invoke`;先用 DEV,只读动作先跑。
|
|
113
|
-
- 不要把 messageblock 原样贴进工单——里面有 `ClientSecret`。
|
|
114
|
-
- 不要因为「步骤没日志」就断定插件没跑:先看跟踪级别,再看异步作业。
|
|
115
|
-
- 不要绕过工具手写 OData/PowerShell 复现:一次 `d365_invoke` 比 15 次 `Invoke-RestMethod` 快,而且不会踩编码与 `$ErrorActionPreference` 的坑。
|
|
1
|
+
---
|
|
2
|
+
name: d365-troubleshooting
|
|
3
|
+
description: 用 dsh-d365-kit 排查 Dynamics 365 / Dataverse 插件失败:插件报错、跟踪日志、步骤注册、消息复现、异步作业。Use when a D365 plug-in, custom action, flow or async job fails and you need to find the cause with the d365_* tools.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 排查 D365 插件失败
|
|
7
|
+
|
|
8
|
+
## 先用对工具(按问题查)
|
|
9
|
+
|
|
10
|
+
| 你手上是这个问题 | 用这个 | 不要用 |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| 这个环境里到底有什么、版本多少 | `d365_envs`(`check` 真连) | 猜 URL |
|
|
13
|
+
| 这张表有哪些字段、某个字段的类型/默认值 | `d365_metadata resource=attributes`(派生列先 `cast`) | `d365_data` |
|
|
14
|
+
| 这个字段为什么表单上没有 / 改了不生效 | `d365_refs kind=field` + `d365_layers` | 只看 `attributes` |
|
|
15
|
+
| 两个环境差在哪 | `d365_sync` 两边 → `d365_diff` | 两边各查一遍对眼 |
|
|
16
|
+
| 目标环境缺哪个程序集/类型/步骤 | `d365_contract drift` | 翻 `plugin_steps` 5000 行 |
|
|
17
|
+
| 这个表/选项集被哪些解决方案盖过 | `d365_layers` | `d365_metadata solutions` |
|
|
18
|
+
| 导入会不会覆盖别人的层 | `d365_migrate` | `d365_diff` |
|
|
19
|
+
| 把一批记录复制到另一个环境 | `d365_data_sync plan` → `execute` | 用 `d365_data_write` 手抄 |
|
|
20
|
+
| 改一批已存在记录的某个字段 | `d365_data_sync plan-update` → `execute` | `d365_data_write` 一条一张卡 |
|
|
21
|
+
| 改/建一条记录 | `d365_data_write` | `d365_write` |
|
|
22
|
+
| 改元数据(字段、显示名、选项值、移除自定义层) | `d365_write`(`plan-*` 只读,`execute` 确认) | 手写 Web API |
|
|
23
|
+
| 这条记录长什么样、有多少条 | `d365_data`(`count`/`group`/`sample`/`query`) | `d365_metadata` |
|
|
24
|
+
| 插件报错,为什么 | `d365_trace summary` → `get`,先 `d365_health` 排除异步作业 | 猜错误码 |
|
|
25
|
+
| 这条消息现在返回什么 | `d365_invoke` | Postman 手拼 |
|
|
26
|
+
| 这条表的逻辑是不是在云流里 / 哪些 flow 会碰它 | `d365_flows refs` → `show` | 在 Power Automate 门户里逐条点开 |
|
|
27
|
+
| 产物太大看不全 | `d365_reports action=view`(`match` 找 GUID) | 全量读文件 |
|
|
28
|
+
|
|
29
|
+
## 什么时候用
|
|
30
|
+
|
|
31
|
+
插件抛异常、自定义消息返回 400/500、某个表上的同步逻辑突然不动、异步作业积压——任何「元数据看起来对,但运行时不对」的问题。目标不是读一堆日志,而是**尽快拿到一个签名 + 一个可复现的调用**。
|
|
32
|
+
|
|
33
|
+
## 先确认三件事(30 秒,别跳)
|
|
34
|
+
|
|
35
|
+
1. **哪个环境**:`d365_envs` 看配置与连通性。环境搞错是最常见的假故障。
|
|
36
|
+
2. **权限够不够**:跟踪日志属于业务数据,要 `data.read`;调 POST 消息要 `message.invoke`(默认关)。
|
|
37
|
+
3. **跟踪有没有开**:`d365_health` 的「插件跟踪」列 = 关闭 / 仅异常 / 全部。列显示「关闭」时,任何 trace 查询都查不到东西——先让环境开起来,再谈排查。
|
|
38
|
+
|
|
39
|
+
## 排查顺序
|
|
40
|
+
|
|
41
|
+
### 1. 先看是不是异步作业,而不是插件
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{ "environments": ["sit"], "hours": 48 }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`d365_health` 的挂起 / 近期失败(Completed 且 Failed)指向 `asyncoperation`。异步作业失败**不会**写插件跟踪日志,这两条线不要混。
|
|
48
|
+
|
|
49
|
+
### 2. 归并签名:几百条失败通常是同一个 bug
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "action": "summary", "hours": 24, "entity": "contact" }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`d365_trace summary` 按「插件类型 + 消息 + 表 + 异常首行」归并。签名一样 = 同一个 bug 在反复发生,接着只查一条就够。
|
|
56
|
+
|
|
57
|
+
### 3. 取一条完整调用
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{ "action": "list", "typename": "ContactSearch", "hours": 24, "top": 20 }
|
|
61
|
+
{ "action": "get", "traceId": "<上一步的 id>" }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`get` 给异常全文 + messageblock(入参)。messageblock 里有 `secureConfig` 的明文密钥,工具会屏蔽成 `***`——**不要**为了「看得清楚」去开 `includeConfig`。
|
|
65
|
+
|
|
66
|
+
### 4. 步骤到底注册在哪
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{ "resource": "plugin_steps", "top": 1000, "filter": "contains(typename,'ContactSearch')" }
|
|
70
|
+
{ "resource": "step_images", "top": 200 }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
看三样:`message`(Create / Update / RetrieveMultiple)、`stage`(20 前 / 40 后)、`mode`(同步 0 / 异步 1),以及 `filteringattributes`。步骤存在但没触发,九成是这三项之一不对,而不是代码不对。
|
|
74
|
+
|
|
75
|
+
### 5. 复现那条消息
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{ "message": "contoso_Recalculate", "method": "POST", "entity": "accounts",
|
|
79
|
+
"id": "<guid>", "parameters": { "Target": { "@odata.type": "Microsoft.Dynamics.CRM.account" } } }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`d365_invoke` **失败不抛异常**,会把 HTTP 状态、Dataverse 错误码和响应体原样给你——这正是插件抛出的原始形状。GET function 走 `data.read`,POST action 走 `message.invoke`,每次都会弹确认卡片。
|
|
83
|
+
|
|
84
|
+
### 6. 需要时再往下挖
|
|
85
|
+
|
|
86
|
+
- 数据是否符合预期:`d365_data { "action": "query", "entity": "contact", "filter": "..." }`
|
|
87
|
+
- 谁引用了这个字段/脚本/程序集:`d365_refs`
|
|
88
|
+
- 一个环境有、另一个环境没有:`d365_contract`(插件步骤/程序集版本漂移)、`d365_layers`(谁盖住了谁)
|
|
89
|
+
- 元数据本身对不上:`d365_metadata resource=attributes ... cast=...`(`DefaultValue`、`OptionSet`、`Targets` 必须先 cast)
|
|
90
|
+
- 逻辑不在插件里、而是被云流接走了:`d365_flows refs entity=<表>` 看哪些 flow 会碰这张表,再 `show` 看它的触发器和步骤(云流的运行历史不在 Dataverse,失败要看 Power Automate 门户)
|
|
91
|
+
|
|
92
|
+
## 错误码速查
|
|
93
|
+
|
|
94
|
+
| 错误码 | 通常意味着 | 下一步 |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `0x80048d19` | 请求体形状不对:把 lookup 写成了裸 GUID,而不是 `字段@odata.bind`(而且键要用导航属性名) | 查 `RelationshipDefinitions` 的 `ReferencingEntityNavigationPropertyName` |
|
|
97
|
+
| `0x80040216` / `0x80040217` | 引用的记录不存在(常见于跨环境复制后 GUID 不匹配) | `d365_data query id=...` 确认;迁移用 `plan` 的 lookup 报告 |
|
|
98
|
+
| `0x80040333` | 重复检测规则拦下 | 确认是不是真重复;批量写时再考虑 `MSCRM.SuppressDuplicateDetection` |
|
|
99
|
+
| `0x80044150` | 并发冲突(别人同时改了同一条) | 重试单条,不要盲目放大并发 |
|
|
100
|
+
| `0x80048423` | 受管组件不可改 | 在未受管解决方案里改,别碰受管层 |
|
|
101
|
+
| `0x8004a0f6` | 状态机不允许这次状态转换 | 走对应消息(如 `CloseIncident`),不要直接改 statuscode |
|
|
102
|
+
| `0x80060888` | 属性名或查询参数不被这个端点支持 | 元数据端点大多不支持 `$filter`/`$orderby`/`$top`;派生列要 cast |
|
|
103
|
+
|
|
104
|
+
## 三个真实踩过的坑
|
|
105
|
+
|
|
106
|
+
- **成功执行的 trace 存空串,不是 null**。只写 `exceptiondetails ne null` 会把成功执行也列成失败——至少多算了 3 倍。`d365_trace` 默认已经排除。
|
|
107
|
+
- **`$count` 在 5000 截断**,`d365_metadata` 显示「≥5000」不是 bug,是服务端上限。要精确总数就分组或加条件。
|
|
108
|
+
- **元数据读和业务数据读是两道闸**:`metadata.read` 默认开,`data.read` 默认关。查 trace 被拒时看的是 `data.read`,不是元数据那一道。
|
|
109
|
+
|
|
110
|
+
## 不要做的事
|
|
111
|
+
|
|
112
|
+
- 不要连生产环境试 `execute`/`invoke`;先用 DEV,只读动作先跑。
|
|
113
|
+
- 不要把 messageblock 原样贴进工单——里面有 `ClientSecret`。
|
|
114
|
+
- 不要因为「步骤没日志」就断定插件没跑:先看跟踪级别,再看异步作业。
|
|
115
|
+
- 不要绕过工具手写 OData/PowerShell 复现:一次 `d365_invoke` 比 15 次 `Invoke-RestMethod` 快,而且不会踩编码与 `$ErrorActionPreference` 的坑。
|