@elf-express/admin-net-mcp 1.0.0 → 1.1.0

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.
Files changed (55) hide show
  1. package/dist/index.js +83 -88
  2. package/knowledge/Furion_Teaching_Manual/04-1-/351/205/215/347/275/256.md +442 -0
  3. package/knowledge/Furion_Teaching_Manual/04-2-/351/201/270/351/240/205.md +363 -0
  4. package/knowledge/Furion_Teaching_Manual/05-1-/345/213/225/346/205/213WebAPI.md +825 -0
  5. package/knowledge/Furion_Teaching_Manual/05-2-HttpContext.md +217 -0
  6. package/knowledge/Furion_Teaching_Manual/05-3-/347/257/251/351/201/270/345/231/250/346/224/224/346/210/252/345/231/250AOP.md +581 -0
  7. package/knowledge/Furion_Teaching_Manual/05-4-/350/253/213/346/261/202/347/250/275/346/240/270/346/227/245/350/252/214.md +129 -0
  8. package/knowledge/Furion_Teaching_Manual/05-5-/344/270/255/344/273/213/350/273/237/351/253/224Middleware.md +328 -0
  9. package/knowledge/Furion_Teaching_Manual/05-6-Vue-React-Angular/344/273/213/351/235/242/344/273/243/347/220/206.md +317 -0
  10. package/knowledge/Furion_Teaching_Manual/06-1/350/246/217/347/257/204/345/214/226/346/216/245/345/217/243.md +1458 -0
  11. package/knowledge/Furion_Teaching_Manual/06-2/347/254/254/344/270/211/346/226/271API_Scalar.md +91 -0
  12. package/knowledge/Furion_Teaching_Manual/07-/345/217/213/345/245/275/344/276/213/345/244/226/350/231/225/347/220/206.md +511 -0
  13. package/knowledge/Furion_Teaching_Manual/08-1-/350/263/207/346/226/231/351/251/227/350/255/211/345/237/272/347/244/216/344/275/277/347/224/250.md +587 -0
  14. package/knowledge/Furion_Teaching_Manual/10-1-SqlSugar/346/225/264/345/220/210.md +336 -0
  15. package/knowledge/Furion_Teaching_Manual/11-SaaS /345/244/232/347/247/237/346/210/266/347/255/206/350/250/230.md" +271 -0
  16. package/knowledge/Furion_Teaching_Manual/12-furion-dependency-injection.md +408 -0
  17. package/knowledge/Furion_Teaching_Manual/13-/347/211/251/344/273/266/350/263/207/346/226/231/346/230/240/345/260/204/357/274/210Mapster/357/274/211.md +162 -0
  18. package/knowledge/Furion_Teaching_Manual/14-/345/210/206/345/270/203/345/274/217/347/274/223/345/255/230.md +311 -0
  19. package/knowledge/Furion_Teaching_Manual/15-/345/256/211/345/205/250/351/211/264/346/235/203.md +832 -0
  20. package/knowledge/Furion_Teaching_Manual/17-/346/252/242/350/246/226/347/257/204/346/234/254/345/274/225/346/223/216.md +327 -0
  21. package/knowledge/Furion_Teaching_Manual/18-/346/227/245/350/252/214/350/250/230/351/214/204.md +639 -0
  22. package/knowledge/Furion_Teaching_Manual/19-1-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/237/272/347/244/216/344/275/277/347/224/250.md +621 -0
  23. package/knowledge/Furion_Teaching_Manual/19-2-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/351/200/262/351/232/216/346/214/207/345/215/227.md +928 -0
  24. package/knowledge/Furion_Teaching_Manual/19-3-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/270/270/350/246/213/345/225/217/351/241/214.md +362 -0
  25. package/knowledge/Furion_Teaching_Manual/20-/350/263/207/346/226/231/345/212/240/350/247/243/345/257/206.md +286 -0
  26. package/knowledge/Furion_Teaching_Manual/20-/351/231/204/351/214/204-KSort/350/263/207/346/226/231/347/260/275/345/220/215/345/256/214/346/225/264/345/216/237/345/247/213/347/242/274.md +305 -0
  27. package/knowledge/Furion_Teaching_Manual/21-/345/205/250/347/220/203/345/214/226/345/222/214/346/234/254/345/234/260/345/214/226.md +342 -0
  28. package/knowledge/Furion_Teaching_Manual/22-/344/272/213/344/273/266/345/214/257/346/265/201/346/216/222EventBus.md +448 -0
  29. package/knowledge/Furion_Teaching_Manual/23-JSON/345/272/217/345/210/227/345/214/226.md +340 -0
  30. package/knowledge/Furion_Teaching_Manual/24-/345/215/263/346/231/202/351/200/232/350/250/212SignalR.md +247 -0
  31. package/knowledge/Furion_Teaching_Manual/25-/350/274/224/345/212/251/350/247/222/350/211/262/346/234/215/345/213/231WorkerService.md +295 -0
  32. package/knowledge/Furion_Teaching_Manual/26-1-/346/216/222/347/250/213/344/275/234/346/245/255/345/256/232/346/231/202/344/273/273/345/213/231.md +631 -0
  33. package/knowledge/Furion_Teaching_Manual/26-2-Cron/350/241/250/351/201/224/345/274/217.md +203 -0
  34. package/knowledge/Furion_Teaching_Manual/26-3-/344/273/273/345/213/231/344/275/207/345/210/227TaskQueue.md +215 -0
  35. package/knowledge/Furion_Teaching_Manual/27-/345/210/206/346/225/243/345/274/217ID/347/224/237/346/210/220.md +86 -0
  36. package/knowledge/Furion_Teaching_Manual/28-/346/250/241/347/265/204/345/214/226/351/226/213/347/231/274.md +68 -0
  37. package/knowledge/Furion_Teaching_Manual/29-/346/265/201/350/256/212/347/211/251/344/273/266Clay.md +313 -0
  38. package/knowledge/Furion_Teaching_Manual/30-/350/204/253/346/225/217/350/231/225/347/220/206/357/274/210Sensitive Detection).md" +168 -0
  39. package/knowledge/Furion_Teaching_Manual/32-/346/234/203/350/251/261/345/222/214/347/213/200/346/205/213/347/256/241/347/220/206.md +147 -0
  40. package/knowledge/Furion_Teaching_Manual/33-IPC/347/250/213/345/272/217/351/200/232/350/250/212.md +98 -0
  41. package/knowledge/Furion_Teaching_Manual/34-2-Docker/351/203/250/347/275/262.md +313 -0
  42. package/knowledge/Furion_Teaching_Manual/34-3-Nginx/351/203/250/347/275/262.md +157 -0
  43. package/knowledge/Furion_Teaching_Manual/34-8-WindowsService/351/203/250/347/275/262.md +112 -0
  44. package/knowledge/Furion_Teaching_Manual/35-1-Docker/347/222/260/345/242/203/346/214/201/347/272/214/351/203/250/347/275/262Jenkins.md +169 -0
  45. package/knowledge/Furion_Teaching_Manual/36-1-/345/226/256/345/205/203/346/270/254/350/251/246/346/225/264/345/220/210/346/270/254/350/251/246.md +275 -0
  46. package/knowledge/Furion_Teaching_Manual/36-3-/345/237/272/346/272/226/346/270/254/350/251/246Benchmarking.md +80 -0
  47. package/knowledge/attributes.md +153 -0
  48. package/knowledge/config.md +147 -0
  49. package/knowledge/entity.md +115 -0
  50. package/knowledge/event.md +124 -0
  51. package/knowledge/plugin.md +136 -0
  52. package/knowledge/service.md +146 -0
  53. package/knowledge/sqlsugar.md +172 -0
  54. package/knowledge/vue-typescript.md +338 -0
  55. package/package.json +3 -2
@@ -0,0 +1,448 @@
1
+ # 22. 事件匯流排(EventBus)
2
+
3
+ ---
4
+
5
+ ## 22.1 概述
6
+
7
+ 事件匯流排是對**發佈-訂閱模式**的一種實作,允許不同元件之間彼此通訊而不需要相互依賴,達到解耦的目的。
8
+
9
+ > Furion 框架已內建此功能。非 Furion 框架可安裝 `Jaina` 套件:`dotnet add package Jaina`
10
+
11
+ ---
12
+
13
+ ## 22.2 快速入門
14
+
15
+ ### 22.2.1 定義事件處理程式
16
+
17
+ ```csharp
18
+ public class ToDoEventSubscriber : IEventSubscriber
19
+ {
20
+ private readonly ILogger<ToDoEventSubscriber> _logger;
21
+ public ToDoEventSubscriber(ILogger<ToDoEventSubscriber> logger) => _logger = logger;
22
+
23
+ [EventSubscribe("ToDo:Create")]
24
+ public async Task CreateToDo(EventHandlerExecutingContext context)
25
+ {
26
+ _logger.LogInformation("建立 ToDo:{Name}", context.Source.Payload);
27
+ // 4.9.5.24+ 支援 context.GetPayload<string>()
28
+ await Task.CompletedTask;
29
+ }
30
+
31
+ // 支援多個事件 Id
32
+ [EventSubscribe("ToDo:Create")]
33
+ [EventSubscribe("ToDo:Update")]
34
+ public async Task CreateOrUpdateToDo(EventHandlerExecutingContext context) { }
35
+
36
+ // 支援列舉型別(3.4.3+)
37
+ [EventSubscribe(YourEnum.Some)]
38
+ public async Task EnumHandler(EventHandlerExecutingContext context)
39
+ {
40
+ var eventEnum = context.Source.EventId.ParseToEnum();
41
+ }
42
+
43
+ // 支援正則表達式匹配(4.2.10+)
44
+ [EventSubscribe("正則表達式", FuzzyMatch = true)]
45
+ public async Task RegexHandler(EventHandlerExecutingContext context) { }
46
+
47
+ // 異常重試設定(4.2.10+)
48
+ [EventSubscribe("test:error", NumRetries = 3)]
49
+ [EventSubscribe("test:error", NumRetries = 3, RetryTimeout = 1000)]
50
+ [EventSubscribe("test:error", NumRetries = 3, ExceptionTypes = new[] { typeof(ArgumentException) })]
51
+ public async Task ExceptionHandler(EventHandlerExecutingContext context) { }
52
+
53
+ // Order 編排(4.8.0+)
54
+ [EventSubscribe("test:order", Order = 1)]
55
+ public async Task OrderedHandler(EventHandlerExecutingContext context) { }
56
+ }
57
+ ```
58
+
59
+ ### 22.2.2 發佈事件訊息
60
+
61
+ ```csharp
62
+ public class ToDoController : ControllerBase
63
+ {
64
+ private readonly IEventPublisher _eventPublisher;
65
+ public ToDoController(IEventPublisher eventPublisher) => _eventPublisher = eventPublisher;
66
+
67
+ public async Task CreateDoTo(string name)
68
+ {
69
+ // 發佈
70
+ await _eventPublisher.PublishAsync(new ChannelEventSource("ToDo:Create", name));
71
+
72
+ // 延遲發佈(3 秒後)
73
+ await _eventPublisher.PublishDelayAsync(new ChannelEventSource("ToDo:Create", name), 3000);
74
+
75
+ // 簡化方式(3.4.3+)
76
+ await _eventPublisher.PublishAsync("ToDo:Create", name);
77
+ await _eventPublisher.PublishDelayAsync("ToDo:Create", 3000, name);
78
+ await _eventPublisher.PublishAsync(YourEnum.Some); // 列舉
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### 22.2.3 註冊事件服務
84
+
85
+ ```csharp
86
+ services.AddEventBus(builder =>
87
+ {
88
+ builder.AddSubscriber<ToDoEventSubscriber>();
89
+ builder.AddSubscriber(typeof(ToDoEventSubscriber));
90
+ builder.AddSubscribers(ass1, ass2);
91
+ });
92
+ ```
93
+
94
+ > **懶人方式**:實作 `ISingleton` 介面即可自動註冊,無需 `AddSubscriber`:
95
+ > ```csharp
96
+ > public class ToDoEventSubscriber : IEventSubscriber, ISingleton { }
97
+ > ```
98
+
99
+ ---
100
+
101
+ ## 22.3 自訂事件源
102
+
103
+ 實作 `IEventSource` 介面:
104
+
105
+ ```csharp
106
+ public class ToDoEventSource : IEventSource
107
+ {
108
+ public string ToDoName { get; set; } // 自訂屬性
109
+ public string EventId { get; set; }
110
+ public object Payload { get; set; }
111
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
112
+ public bool ConsumeOnce { get; set; } // 4.9.1.24+:僅消費一次
113
+
114
+ [JsonIgnore]
115
+ public CancellationToken CancellationToken { get; set; }
116
+ }
117
+
118
+ // 使用
119
+ await _eventPublisher.PublishAsync(new ToDoEventSource("ToDo:Create", "我的 ToDo"));
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 22.4 自訂事件源儲存器
125
+
126
+ 預設使用 `Channel`,可替換為 Redis、RabbitMQ、Kafka 等。實作 `IEventSourceStorer` 介面即可。
127
+
128
+ ### 22.4.1 Redis
129
+
130
+ ```bash
131
+ Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
132
+ ```
133
+
134
+ 核心邏輯:Redis Pub/Sub 接收訊息 → 反序列化為 `ChannelEventSource` → 寫入記憶體 `Channel`;發佈時序列化後透過 Redis Publish 傳送。
135
+
136
+ ```csharp
137
+ services.AddEventBus(options =>
138
+ {
139
+ var conn = ConnectionMultiplexer.Connect("localhost");
140
+ var storer = new RedisEventSourceStorer(conn, "routeKey", 12000);
141
+ options.ReplaceStorer(sp => storer);
142
+ });
143
+ ```
144
+
145
+ ### 22.4.2 RabbitMQ
146
+
147
+ ```bash
148
+ Install-Package RabbitMQ.Client
149
+ ```
150
+
151
+ 核心邏輯:RabbitMQ Consumer 接收訊息 → 反序列化 → 寫入記憶體 Channel + BasicAck;發佈時序列化後 BasicPublish。
152
+
153
+ ```csharp
154
+ services.AddEventBus(options =>
155
+ {
156
+ var factory = new ConnectionFactory { UserName = "admin", Password = "q1w2e3" };
157
+ var storer = new RabbitMQEventSourceStorer(factory, "routeKey", 12000);
158
+ options.ReplaceStorer(sp => storer);
159
+ });
160
+ ```
161
+
162
+ ### 22.4.3 Kafka
163
+
164
+ ```bash
165
+ Install-Package Confluent.Kafka
166
+ ```
167
+
168
+ 核心邏輯:Kafka Consumer 訂閱 Topic → 反序列化 → 寫入記憶體 Channel;發佈時透過 Producer 非同步發送。
169
+
170
+ ```csharp
171
+ services.AddEventBus(options =>
172
+ {
173
+ var consumerConf = new ConsumerConfig { BootstrapServers = "xxx:9092", GroupId = "Consumer", AutoOffsetReset = AutoOffsetReset.Earliest };
174
+ var producerConf = new ProducerConfig { BootstrapServers = "xxx:9092" };
175
+ var storer = new KafkaEventSourceStore(consumerConf, producerConf, "topic", 12000);
176
+ options.ReplaceStorer(sp => storer);
177
+ });
178
+ ```
179
+
180
+ > 完整 Redis / RabbitMQ / Kafka 儲存器原始碼請參閱 Furion 官方文件。
181
+
182
+ ---
183
+
184
+ ## 22.5 自訂事件發佈者
185
+
186
+ ```csharp
187
+ public class ToDoEventPublisher : IEventPublisher
188
+ {
189
+ private readonly IEventSourceStorer _storer;
190
+ public ToDoEventPublisher(IEventSourceStorer storer) => _storer = storer;
191
+
192
+ public async Task PublishAsync(IEventSource eventSource)
193
+ => await _storer.WriteAsync(eventSource, eventSource.CancellationToken);
194
+ }
195
+
196
+ services.AddEventBus(builder => builder.ReplacePublisher<ToDoEventPublisher>());
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 22.6 事件執行監視器
202
+
203
+ ```csharp
204
+ public class ToDoEventHandlerMonitor : IEventHandlerMonitor
205
+ {
206
+ private readonly ILogger<ToDoEventHandlerMonitor> _logger;
207
+ public ToDoEventHandlerMonitor(ILogger<ToDoEventHandlerMonitor> logger) => _logger = logger;
208
+
209
+ public Task OnExecutingAsync(EventHandlerExecutingContext context)
210
+ {
211
+ _logger.LogInformation("執行之前:{EventId}", context.Source.EventId);
212
+ return Task.CompletedTask;
213
+ }
214
+
215
+ public Task OnExecutedAsync(EventHandlerExecutedContext context)
216
+ {
217
+ _logger.LogInformation("執行之後:{EventId}", context.Source.EventId);
218
+ if (context.Exception != null)
219
+ _logger.LogError(context.Exception, "執行出錯:{EventId}", context.Source.EventId);
220
+ return Task.CompletedTask;
221
+ }
222
+ }
223
+
224
+ services.AddEventBus(builder => builder.AddMonitor<ToDoEventHandlerMonitor>());
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 22.7 事件執行器
230
+
231
+ 自訂執行策略(逾時控制、失敗重試、熔斷等):
232
+
233
+ ```csharp
234
+ public class RetryEventHandlerExecutor : IEventHandlerExecutor
235
+ {
236
+ public async Task ExecuteAsync(EventHandlerExecutingContext context, Func<EventHandlerExecutingContext, Task> handler)
237
+ {
238
+ await Retry.InvokeAsync(async () => await handler(context), 3, 1000);
239
+ }
240
+ }
241
+
242
+ services.AddEventBus(builder => builder.AddExecutor<RetryEventHandlerExecutor>());
243
+ ```
244
+
245
+ ---
246
+
247
+ ## 22.8 使用有作用域的服務
248
+
249
+ EventBus 所有服務均為**單例**,需使用作用域服務時:
250
+
251
+ ```csharp
252
+ public class ToDoEventSubscriber : IEventSubscriber
253
+ {
254
+ private readonly IServiceScopeFactory _scopeFactory;
255
+ public ToDoEventSubscriber(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
256
+
257
+ [EventSubscribe("ToDo:Create")]
258
+ public async Task CreateToDo(EventHandlerExecutingContext context)
259
+ {
260
+ using var scope = _scopeFactory.CreateScope();
261
+ var service = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
262
+ }
263
+ }
264
+ ```
265
+
266
+ > ⚠️ 避免在建構子中解析非單例服務。
267
+
268
+ ---
269
+
270
+ ## 22.9 訂閱未察覺異常
271
+
272
+ ```csharp
273
+ services.AddEventBus(builder =>
274
+ {
275
+ builder.UnobservedTaskExceptionHandler = (obj, args) => { /* 處理 */ };
276
+ });
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 22.10 事件匯流排工廠(4.2.10+)
282
+
283
+ ```csharp
284
+ // 動態新增訂閱
285
+ await _eventBusFactory.Subscribe("xxx", async ctx => { Console.WriteLine("動態訂閱"); });
286
+
287
+ // 動態移除訂閱
288
+ await _eventBusFactory.Unsubscribe(eventId);
289
+ ```
290
+
291
+ ---
292
+
293
+ ## 22.11 MessageCenter 靜態類(4.3.3+)
294
+
295
+ ```csharp
296
+ await MessageCenter.PublishAsync("messageId", new {});
297
+
298
+ MessageCenter.Subscribe("messageId", async ctx => { });
299
+
300
+ MessageCenter.Unsubscribe("messageId");
301
+ ```
302
+
303
+ ---
304
+
305
+ ## 22.12 重試失敗回呼(4.6.1+)
306
+
307
+ ```csharp
308
+ public class EventFallbackPolicy : IEventFallbackPolicy
309
+ {
310
+ public async Task CallbackAsync(EventHandlerExecutingContext context, Exception ex)
311
+ {
312
+ // 重試多次仍失敗時的處理
313
+ }
314
+ }
315
+
316
+ services.AddEventBus(options => options.AddFallbackPolicy<EventFallbackPolicy>());
317
+
318
+ [EventSubscribe("test:error", NumRetries = 3, FallbackPolicy = typeof(EventFallbackPolicy))]
319
+ public async Task TestError(EventHandlerExecutingContext context) { throw new NotImplementedException(); }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## 22.13 儲存器不可用時回退
325
+
326
+ ```csharp
327
+ services.AddEventBus(options =>
328
+ {
329
+ options.ReplaceStorerOrFallback(sp => new RedisEventSourceStorer(...));
330
+ });
331
+ ```
332
+
333
+ > 僅支援啟動時檢查,不支援執行階段故障轉移。
334
+
335
+ ---
336
+
337
+ ## 22.14 EventBusOptionsBuilder 設定
338
+
339
+ | 屬性 / 方法 | 說明 |
340
+ |-------------|------|
341
+ | `ChannelCapacity` | 記憶體通道容量,預設 `12000` |
342
+ | `UnobservedTaskExceptionHandler` | 未察覺異常處理 |
343
+ | `UseUtcTimestamp` | 使用 UTC 時間,預設 `false` |
344
+ | `FuzzyMatch` | 全域模糊匹配(正則),預設 `false` |
345
+ | `LogEnabled` | 啟用日誌,預設 `true` |
346
+ | `AddSubscriber<T>()` | 新增訂閱者 |
347
+ | `ReplacePublisher<T>()` | 替換發佈者 |
348
+ | `ReplaceStorer(Func<...>)` | 替換儲存器 |
349
+ | `ReplaceStorerOrFallback(Func<...>)` | 替換儲存器(失敗回退預設) |
350
+ | `AddMonitor<T>()` | 新增監視器 |
351
+ | `AddExecutor<T>()` | 新增執行器 |
352
+ | `AddFallbackPolicy<T>()` | 新增重試失敗回呼策略 |
353
+
354
+ ---
355
+
356
+ ## 22.15 高頻訊息處理
357
+
358
+ IoT、日誌記錄等高頻場景,使用**類全域作用域**避免頻繁建立/銷毀:
359
+
360
+ ```csharp
361
+ public class ToDoEventSubscriber : IEventSubscriber, IDisposable
362
+ {
363
+ private readonly IServiceScope _serviceScope;
364
+
365
+ public ToDoEventSubscriber(IServiceScopeFactory scopeFactory)
366
+ => _serviceScope = scopeFactory.CreateScope();
367
+
368
+ [EventSubscribe("iot:log")]
369
+ public async Task LogFromIoT(EventHandlerExecutingContext context)
370
+ {
371
+ var service = _serviceScope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
372
+ }
373
+
374
+ public void Dispose() => _serviceScope.Dispose();
375
+ }
376
+ ```
377
+
378
+ ---
379
+
380
+ ## 22.16 事件執行結果訂閱(4.9.1.47+)
381
+
382
+ ```csharp
383
+ _eventPublisher.OnExecuted += (sender, args) =>
384
+ {
385
+ Console.WriteLine($"事件 {args.Source.EventId} 狀態:{args.Status},例外:{args.Exception},結果:{args.Result}");
386
+ // args.Result 為 4.9.7.61+ 新增
387
+ };
388
+ ```
389
+
390
+ > 推薦在 `Startup.cs` 的 `Configure` 中或**單例服務**中訂閱,避免重複多播訂閱。非單例服務中訂閱後需在 `Dispose` 中移除。
391
+
392
+ ---
393
+
394
+ ## 22.17 IIS 部署
395
+
396
+ 部署到 IIS 時需**停用應用程式集區回收**,避免事件匯流排服務進入休眠。
397
+
398
+ ---
399
+
400
+ ## 22.18 第三方 CAP 整合
401
+
402
+ ```csharp
403
+ builder.Services.AddCap(options =>
404
+ {
405
+ options.UseInMemoryStorage();
406
+ options.UseInMemoryMessageQueue();
407
+ }).AddSubscriberAssembly(App.Assemblies.ToArray());
408
+ ```
409
+
410
+ ---
411
+
412
+ ## 22.19 在 WinForm / WPF 中使用
413
+
414
+ ```csharp
415
+ // 發送事件(將控制項作為 Payload)
416
+ await _eventPublisher.PublishAsync("update.textbox", textBox1);
417
+
418
+ // 訂閱並更新 UI
419
+ [EventSubscribe("update.textbox")]
420
+ public async Task UpdateTextBox(EventHandlerExecutingContext context)
421
+ {
422
+ var textbox = context.GetPayload<TextBox>(); // 4.9.5.24+
423
+
424
+ // WinForm
425
+ textbox?.BeginInvoke(() => textbox.Text = "事件匯流排更新");
426
+
427
+ // WPF
428
+ textbox?.Dispatcher?.BeginInvoke(() => textbox.Content = "事件匯流排更新");
429
+ }
430
+ ```
431
+
432
+ ---
433
+
434
+ ## 更新日誌摘要
435
+
436
+ | 版本 | 日期 | 內容 |
437
+ |------|------|------|
438
+ | 4.9.8.13 | 2026.02.06 | `IsConsumOnce` 更名為 `ConsumeOnce` |
439
+ | 4.9.7.243 | 2026.01.07 | 支援取得 `RunId` |
440
+ | 4.9.7.83 | 2025.06.10 | 支援 `GetPayload<Clay>`、修復主機停止異常 |
441
+ | 4.9.7.61 | 2025.05.12 | 支援 `context.SetResult(object)` |
442
+ | 4.9.5.24 | 2024.11.20 | 新增 `context.GetPayload<T>()` |
443
+ | 4.9.1.47 | 2024.03.13 | 執行結果訂閱 `OnExecuted` |
444
+ | 4.9.1.24 | 2024.01.19 | 支援僅消費一次 `ConsumeOnce` |
445
+ | 4.8.0 | — | 簡單 Order 編排 |
446
+ | 4.6.1 | — | 重試失敗回呼 |
447
+ | 4.3.3 | — | `MessageCenter` 靜態類 |
448
+ | 4.2.10 | — | 事件工廠動態訂閱/移除、正則匹配、局部重試 |