@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.
- package/dist/index.js +83 -88
- package/knowledge/Furion_Teaching_Manual/04-1-/351/205/215/347/275/256.md +442 -0
- package/knowledge/Furion_Teaching_Manual/04-2-/351/201/270/351/240/205.md +363 -0
- package/knowledge/Furion_Teaching_Manual/05-1-/345/213/225/346/205/213WebAPI.md +825 -0
- package/knowledge/Furion_Teaching_Manual/05-2-HttpContext.md +217 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/05-5-/344/270/255/344/273/213/350/273/237/351/253/224Middleware.md +328 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/06-2/347/254/254/344/270/211/346/226/271API_Scalar.md +91 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/10-1-SqlSugar/346/225/264/345/220/210.md +336 -0
- 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
- package/knowledge/Furion_Teaching_Manual/12-furion-dependency-injection.md +408 -0
- 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
- package/knowledge/Furion_Teaching_Manual/14-/345/210/206/345/270/203/345/274/217/347/274/223/345/255/230.md +311 -0
- package/knowledge/Furion_Teaching_Manual/15-/345/256/211/345/205/250/351/211/264/346/235/203.md +832 -0
- 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
- package/knowledge/Furion_Teaching_Manual/18-/346/227/245/350/252/214/350/250/230/351/214/204.md +639 -0
- 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
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/20-/350/263/207/346/226/231/345/212/240/350/247/243/345/257/206.md +286 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/22-/344/272/213/344/273/266/345/214/257/346/265/201/346/216/222EventBus.md +448 -0
- package/knowledge/Furion_Teaching_Manual/23-JSON/345/272/217/345/210/227/345/214/226.md +340 -0
- package/knowledge/Furion_Teaching_Manual/24-/345/215/263/346/231/202/351/200/232/350/250/212SignalR.md +247 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/26-2-Cron/350/241/250/351/201/224/345/274/217.md +203 -0
- package/knowledge/Furion_Teaching_Manual/26-3-/344/273/273/345/213/231/344/275/207/345/210/227TaskQueue.md +215 -0
- package/knowledge/Furion_Teaching_Manual/27-/345/210/206/346/225/243/345/274/217ID/347/224/237/346/210/220.md +86 -0
- package/knowledge/Furion_Teaching_Manual/28-/346/250/241/347/265/204/345/214/226/351/226/213/347/231/274.md +68 -0
- package/knowledge/Furion_Teaching_Manual/29-/346/265/201/350/256/212/347/211/251/344/273/266Clay.md +313 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/33-IPC/347/250/213/345/272/217/351/200/232/350/250/212.md +98 -0
- package/knowledge/Furion_Teaching_Manual/34-2-Docker/351/203/250/347/275/262.md +313 -0
- package/knowledge/Furion_Teaching_Manual/34-3-Nginx/351/203/250/347/275/262.md +157 -0
- package/knowledge/Furion_Teaching_Manual/34-8-WindowsService/351/203/250/347/275/262.md +112 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/36-3-/345/237/272/346/272/226/346/270/254/350/251/246Benchmarking.md +80 -0
- package/knowledge/attributes.md +153 -0
- package/knowledge/config.md +147 -0
- package/knowledge/entity.md +115 -0
- package/knowledge/event.md +124 -0
- package/knowledge/plugin.md +136 -0
- package/knowledge/service.md +146 -0
- package/knowledge/sqlsugar.md +172 -0
- package/knowledge/vue-typescript.md +338 -0
- package/package.json +3 -2
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Furion 依賴注入 / 控制反轉(DI / IoC)筆記
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 1. 核心概念
|
|
6
|
+
|
|
7
|
+
| 概念 | 簡稱 | 說明 |
|
|
8
|
+
|------|------|------|
|
|
9
|
+
| 依賴注入 | DI | 不在程式碼中手動 `new`,由外部容器自動注入所需物件 |
|
|
10
|
+
| 控制反轉 | IoC | 將建立物件實例的控制權,從程式碼交給 IoC 容器管理 |
|
|
11
|
+
|
|
12
|
+
**優點**:解耦、易擴展、易單元測試
|
|
13
|
+
**缺點**:採用反射實現,有一定效能損耗
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. 三種注入方式
|
|
18
|
+
|
|
19
|
+
### 2.1 建構子注入(推薦)
|
|
20
|
+
|
|
21
|
+
```csharp
|
|
22
|
+
public class FurionService
|
|
23
|
+
{
|
|
24
|
+
private readonly IRepository _repository;
|
|
25
|
+
|
|
26
|
+
public FurionService(IRepository repository)
|
|
27
|
+
{
|
|
28
|
+
_repository = repository;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
✅ 依賴關係清晰,物件初始化後狀態正確
|
|
34
|
+
❌ 參數可能過多;有些類(如 MVC Controller)不適合
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### 2.2 屬性注入
|
|
39
|
+
|
|
40
|
+
```csharp
|
|
41
|
+
public class FurionService
|
|
42
|
+
{
|
|
43
|
+
[FromServices] // .NET 8+ 也支援 [FromKeyedServices]
|
|
44
|
+
public IRepository Repository { get; set; }
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
✅ 靈活,可隨時替換依賴
|
|
49
|
+
❌ 僅限 Controller / 動態 WebAPI;狀態不一致風險
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 2.3 方法參數注入
|
|
54
|
+
|
|
55
|
+
```csharp
|
|
56
|
+
public class FurionService
|
|
57
|
+
{
|
|
58
|
+
public Person GetById([FromServices] IRepository repository, int id)
|
|
59
|
+
{
|
|
60
|
+
return repository.Find(id);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
✅ 靈活
|
|
66
|
+
❌ 僅限 Controller / 動態 WebAPI;新增依賴會破壞方法簽章
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 3. 生命週期
|
|
71
|
+
|
|
72
|
+
| 介面 | 生命週期 | 說明 |
|
|
73
|
+
|------|----------|------|
|
|
74
|
+
| `ITransient` | 暫時(Transient) | 每次請求都建立新實例,適合輕量無狀態服務 |
|
|
75
|
+
| `IScoped` | 範圍(Scoped) | 每個 HTTP 請求建立一次,請求結束後釋放 |
|
|
76
|
+
| `ISingleton` | 單例(Singleton) | 首次請求後全程共用同一實例 |
|
|
77
|
+
|
|
78
|
+
> ⚠️ 以上介面只能由**實例類**實現,靜態類、抽象類、介面不可實現。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 4. 常見用法
|
|
83
|
+
|
|
84
|
+
### 4.1 基本用法(介面 + 實作)
|
|
85
|
+
|
|
86
|
+
```csharp
|
|
87
|
+
// Service 層
|
|
88
|
+
public interface IBusinessService { Person Get(int id); }
|
|
89
|
+
|
|
90
|
+
public class BusinessService : IBusinessService, ITransient
|
|
91
|
+
{
|
|
92
|
+
private readonly IRepository<Person> _personRepository;
|
|
93
|
+
|
|
94
|
+
public BusinessService(IRepository<Person> personRepository)
|
|
95
|
+
{
|
|
96
|
+
_personRepository = personRepository;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public Person Get(int id) => _personRepository.Find(id);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Controller 層
|
|
103
|
+
public class PersonController : ControllerBase
|
|
104
|
+
{
|
|
105
|
+
private readonly IBusinessService _businessService;
|
|
106
|
+
|
|
107
|
+
public PersonController(IBusinessService businessService)
|
|
108
|
+
{
|
|
109
|
+
_businessService = businessService;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### 4.2 泛型注入
|
|
117
|
+
|
|
118
|
+
```csharp
|
|
119
|
+
public class BusinessService<T> : IBusinessService<T>, ITransient { ... }
|
|
120
|
+
|
|
121
|
+
// 注入
|
|
122
|
+
public PersonController(IBusinessService<int> businessService) { ... }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### 4.3 一個介面多個實作
|
|
128
|
+
|
|
129
|
+
**推薦:`INamedServiceProvider`(Furion 3.8.6+)**
|
|
130
|
+
|
|
131
|
+
```csharp
|
|
132
|
+
public class BusinessService : IBusinessService, ITransient { ... }
|
|
133
|
+
public class OtherBusinessService : IBusinessService, ITransient { ... }
|
|
134
|
+
|
|
135
|
+
// 注入並解析
|
|
136
|
+
public class MyApi : IDynamicApiController
|
|
137
|
+
{
|
|
138
|
+
private readonly INamedServiceProvider<IBusinessService> _provider;
|
|
139
|
+
|
|
140
|
+
public MyApi(INamedServiceProvider<IBusinessService> provider)
|
|
141
|
+
{
|
|
142
|
+
_provider = provider;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public string GetName()
|
|
146
|
+
{
|
|
147
|
+
// 方法一:透過反射(有效能損耗)
|
|
148
|
+
var s1 = _provider.GetService(nameof(BusinessService));
|
|
149
|
+
|
|
150
|
+
// 方法二:指定生命週期(無反射,推薦)
|
|
151
|
+
var s2 = _provider.GetService<ITransient>(nameof(OtherBusinessService));
|
|
152
|
+
|
|
153
|
+
return s1.GetName() + s2.GetName();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**自訂解析名稱:`[Injection(Named = "...")]`**
|
|
159
|
+
|
|
160
|
+
```csharp
|
|
161
|
+
[Injection(Named = "BusName1")]
|
|
162
|
+
public class BusinessService : IBusinessService, ITransient { ... }
|
|
163
|
+
|
|
164
|
+
[Injection(Named = "BusName2")]
|
|
165
|
+
public class OtherBusinessService : IBusinessService, ITransient { ... }
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### 4.4 無介面注入(直接注入實作類)
|
|
171
|
+
|
|
172
|
+
```csharp
|
|
173
|
+
public class SelfService : ITransient
|
|
174
|
+
{
|
|
175
|
+
// ...
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Controller 直接注入實作類
|
|
179
|
+
public ValueController(SelfService selfService) { ... }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 5. `[Injection]` 特性配置
|
|
185
|
+
|
|
186
|
+
| 屬性 | 類型 | 說明 |
|
|
187
|
+
|------|------|------|
|
|
188
|
+
| `Action` | `InjectionActions` | `Add`(預設,允許多實作)/ `TryAdd`(已存在則跳過) |
|
|
189
|
+
| `Pattern` | `InjectionPatterns` | `Self` / `FirstInterface` / `SelfWithFirstInterface` / `ImplementedInterfaces` / `All`(預設) |
|
|
190
|
+
| `Named` | `string` | 別名,預設為類名 |
|
|
191
|
+
| `Order` | `int` | 注冊排序,數字越大越晚注冊 |
|
|
192
|
+
| `Proxy` | `Type` | AOP 代理類型 |
|
|
193
|
+
| `ExceptInterfaces` | `Type[]` | 排除不注冊的介面 |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 6. appsettings.json 動態注冊
|
|
198
|
+
|
|
199
|
+
可不修改程式碼,直接透過設定檔實現熱插拔:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"DependencyInjectionSettings": {
|
|
204
|
+
"Definitions": [
|
|
205
|
+
{
|
|
206
|
+
"Interface": "Furion.Application;Furion.Application.ITestService",
|
|
207
|
+
"Service": "Furion.Application;Furion.Application.TestService",
|
|
208
|
+
"RegisterType": "Transient",
|
|
209
|
+
"Action": "Add",
|
|
210
|
+
"Pattern": "SelfWithFirstInterface",
|
|
211
|
+
"Named": "TestService",
|
|
212
|
+
"Order": 1,
|
|
213
|
+
"Proxy": "Furion.Application;Furion.Application.LogDispatchProxy"
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
> 💡 格式:`程式集名稱;完整類別名稱`
|
|
221
|
+
> 💡 外部程式集需先在 `AppSettings.ExternalAssemblies` 中註冊
|
|
222
|
+
|
|
223
|
+
**注冊優先級**:`appsettings.json` > `[Injection]` > 預設掃描
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 7. AOP 攔截
|
|
228
|
+
|
|
229
|
+
> ⚠️ 僅支援**介面注入**,不支援直接注入類。動態 API 請改用 Filter 方式。
|
|
230
|
+
|
|
231
|
+
### 7.1 建立代理類
|
|
232
|
+
|
|
233
|
+
```csharp
|
|
234
|
+
public class LogDispatchProxy : AspectDispatchProxy, IDispatchProxy
|
|
235
|
+
{
|
|
236
|
+
public object Target { get; set; }
|
|
237
|
+
public IServiceProvider Services { get; set; }
|
|
238
|
+
|
|
239
|
+
// 同步攔截
|
|
240
|
+
public override object Invoke(MethodInfo method, object[] args)
|
|
241
|
+
{
|
|
242
|
+
Console.WriteLine("方法被呼叫");
|
|
243
|
+
var result = method.Invoke(Target, args);
|
|
244
|
+
Console.WriteLine("返回值:" + result);
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 非同步無回傳值
|
|
249
|
+
public async override Task InvokeAsync(MethodInfo method, object[] args)
|
|
250
|
+
{
|
|
251
|
+
Console.WriteLine("方法被呼叫");
|
|
252
|
+
await (method.Invoke(Target, args) as Task);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 非同步有回傳值
|
|
256
|
+
public async override Task<T> InvokeAsyncT<T>(MethodInfo method, object[] args)
|
|
257
|
+
{
|
|
258
|
+
var result = await (method.Invoke(Target, args) as Task<T>);
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
> 💡 取得方法特性:`method.GetActualCustomAttribute<TAttribute>()`
|
|
265
|
+
|
|
266
|
+
### 7.2 套用代理
|
|
267
|
+
|
|
268
|
+
```csharp
|
|
269
|
+
[Injection(Proxy = typeof(LogDispatchProxy))]
|
|
270
|
+
public class TestService : ITestService, ITransient { ... }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 7.3 全域攔截
|
|
274
|
+
|
|
275
|
+
```csharp
|
|
276
|
+
// IDispatchProxy 改為 IGlobalDispatchProxy
|
|
277
|
+
public class LogDispatchProxy : AspectDispatchProxy, IGlobalDispatchProxy { ... }
|
|
278
|
+
|
|
279
|
+
// 排除特定類
|
|
280
|
+
[SuppressProxy]
|
|
281
|
+
public class SkipService : ISkipService, ITransient { ... }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**攔截優先級**:`[SuppressProxy]` > `[Injection(Proxy)]` > 全域攔截
|
|
285
|
+
|
|
286
|
+
### 7.4 在 AOP 中解析服務
|
|
287
|
+
|
|
288
|
+
```csharp
|
|
289
|
+
var someService = Services.GetService<ISomeService>(); // 推薦
|
|
290
|
+
// 或
|
|
291
|
+
var someService = App.GetService<ISomeService>();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 8. 非 Web / 多執行緒中解析服務
|
|
297
|
+
|
|
298
|
+
Web 請求外框架不會自動管理作用域,需手動建立:
|
|
299
|
+
|
|
300
|
+
```csharp
|
|
301
|
+
// 方式一:IServiceProvider
|
|
302
|
+
using var scope = serviceProvider.CreateScope();
|
|
303
|
+
var services = scope.ServiceProvider;
|
|
304
|
+
var repo = Db.GetRepository<Person>(services);
|
|
305
|
+
var other = services.GetService<XXX>();
|
|
306
|
+
|
|
307
|
+
// 方式二:IServiceScopeFactory
|
|
308
|
+
using var scope = serviceScopeFactory.CreateScope();
|
|
309
|
+
var services = scope.ServiceProvider;
|
|
310
|
+
|
|
311
|
+
// 方式三:Scoped 靜態類(Furion 提供)
|
|
312
|
+
Scoped.Create((factory, scope) => {
|
|
313
|
+
var services = scope.ServiceProvider;
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 9. 在單例中解析非單例服務
|
|
320
|
+
|
|
321
|
+
透過 `IServiceScopeFactory` 建立範圍再解析:
|
|
322
|
+
|
|
323
|
+
```csharp
|
|
324
|
+
public class SomeService : ISomeService, ISingleton
|
|
325
|
+
{
|
|
326
|
+
private readonly IServiceScopeFactory _scopeFactory;
|
|
327
|
+
|
|
328
|
+
public SomeService(IServiceScopeFactory scopeFactory)
|
|
329
|
+
{
|
|
330
|
+
_scopeFactory = scopeFactory;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public async Task Method()
|
|
334
|
+
{
|
|
335
|
+
using var scope = _scopeFactory.CreateScope();
|
|
336
|
+
var repo = scope.ServiceProvider.GetService<IRepository<User>>();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 10. WinForm / WPF 支援(Furion 4.8.7.23+)
|
|
344
|
+
|
|
345
|
+
將所有 `new Form()` / `new Window()` 替換為 `Native.CreateInstance<T>()`。
|
|
346
|
+
|
|
347
|
+
### WinForm
|
|
348
|
+
|
|
349
|
+
```csharp
|
|
350
|
+
// Program.cs
|
|
351
|
+
static void Main()
|
|
352
|
+
{
|
|
353
|
+
Serve.RunNative();
|
|
354
|
+
ApplicationConfiguration.Initialize();
|
|
355
|
+
Application.Run(Native.CreateInstance<Form1>()); // ← 替換 new Form1()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 開啟新視窗(支援傳入其他參數)
|
|
359
|
+
Native.CreateInstance<Form2>("我是參數").ShowDialog();
|
|
360
|
+
|
|
361
|
+
// 取得已開啟視窗實例
|
|
362
|
+
var form1 = Application.OpenForms.OfType<Form>().FirstOrDefault(f => f is Form1);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### WPF
|
|
366
|
+
|
|
367
|
+
```csharp
|
|
368
|
+
// App.xaml.cs(移除 StartupUri)
|
|
369
|
+
public partial class App : Application
|
|
370
|
+
{
|
|
371
|
+
public App() { Serve.RunNative(); }
|
|
372
|
+
|
|
373
|
+
protected override void OnStartup(StartupEventArgs e)
|
|
374
|
+
{
|
|
375
|
+
Native.CreateInstance<MainWindow>().Show();
|
|
376
|
+
base.OnStartup(e);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// 開啟新視窗
|
|
381
|
+
Native.CreateInstance<Window1>("我是參數").Show();
|
|
382
|
+
|
|
383
|
+
// 取得已開啟視窗實例
|
|
384
|
+
var w = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w is MainWindow);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
> 💡 透過 `Native.CreateInstance<T>()` 建立的視窗支援建構子注入。
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 11. 自訂掃描注冊(Scrutor)
|
|
392
|
+
|
|
393
|
+
```csharp
|
|
394
|
+
services.Scan(scan => scan
|
|
395
|
+
.FromAssemblyOf<ITransientService>()
|
|
396
|
+
.AddClasses(c => c.AssignableTo<ITransientService>())
|
|
397
|
+
.AsImplementedInterfaces()
|
|
398
|
+
.WithTransientLifetime()
|
|
399
|
+
.AddClasses(c => c.AssignableTo<IScopedService>())
|
|
400
|
+
.As<IScopedService>()
|
|
401
|
+
.WithScopedLifetime()
|
|
402
|
+
// 泛型注冊
|
|
403
|
+
.AddClasses(c => c.AssignableTo(typeof(IOpenGeneric<>)))
|
|
404
|
+
.AsImplementedInterfaces()
|
|
405
|
+
);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
> 📖 詳見 [Scrutor GitHub](https://github.com/khellang/Scrutor)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Furion 物件資料映射(Mapster)筆記
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 1. 概念說明
|
|
6
|
+
|
|
7
|
+
將一個物件的資料,依照特定規則批量映射到另一個物件,減少手工賦值操作與人為出錯率。常見場景:`DTO ↔ Entity` 互轉。
|
|
8
|
+
|
|
9
|
+
> 💡 Furion 推薦使用 **Mapster**(高效能、易使用)。若需使用 AutoMapper,無需安裝 Furion 的 Mapster 擴展包。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 2. 安裝
|
|
14
|
+
|
|
15
|
+
在 `Furion.Core` 層安裝擴展包,Furion 會自動載入,無需手動呼叫:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Furion.Extras.ObjectMapper.Mapster
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 3. 基本用法
|
|
24
|
+
|
|
25
|
+
### 3.1 傳統手動賦值(舊寫法)
|
|
26
|
+
|
|
27
|
+
```csharp
|
|
28
|
+
var entity = repository.Find(1);
|
|
29
|
+
|
|
30
|
+
var dto = new Dto();
|
|
31
|
+
dto.Id = entity.Id;
|
|
32
|
+
dto.Name = entity.Name;
|
|
33
|
+
dto.Age = entity.Age;
|
|
34
|
+
dto.FullName = entity.FirstName + entity.LastName;
|
|
35
|
+
dto.IdCard = entity.IdCard.Replace("1234", "****");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3.2 使用 Mapster(新寫法)
|
|
39
|
+
|
|
40
|
+
```csharp
|
|
41
|
+
var entity = repository.Find(1);
|
|
42
|
+
var dto = entity.Adapt<Dto>(); // 一行搞定
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 4. 自訂映射規則
|
|
48
|
+
|
|
49
|
+
對於需要特殊處理的欄位(如組合欄位、資料遮罩),建立映射規則類:
|
|
50
|
+
|
|
51
|
+
```csharp
|
|
52
|
+
using Mapster;
|
|
53
|
+
|
|
54
|
+
public class Mapper : IRegister
|
|
55
|
+
{
|
|
56
|
+
public void Register(TypeAdapterConfig config)
|
|
57
|
+
{
|
|
58
|
+
config.ForType<Entity, Dto>()
|
|
59
|
+
.Map(dest => dest.FullName, src => src.FirstName + src.LastName)
|
|
60
|
+
.Map(dest => dest.IdCard, src => src.IdCard.Replace("1234", "****"));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> 💡 `Mapper.cs` 可放在任意專案或資料夾,Furion 啟動時會自動掃描並注入設定。
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 5. 依賴注入方式
|
|
70
|
+
|
|
71
|
+
```csharp
|
|
72
|
+
public class PersonService
|
|
73
|
+
{
|
|
74
|
+
private readonly IMapper _mapper;
|
|
75
|
+
|
|
76
|
+
public PersonService(IMapper mapper)
|
|
77
|
+
{
|
|
78
|
+
_mapper = mapper;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public Dto GetDto(Entity entity)
|
|
82
|
+
{
|
|
83
|
+
return _mapper.Map<Dto>(entity);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 6. 搭配 EF Core(ProjectToType)
|
|
91
|
+
|
|
92
|
+
避免手動 `Select` 逐欄位對應:
|
|
93
|
+
|
|
94
|
+
```csharp
|
|
95
|
+
// 傳統寫法
|
|
96
|
+
var list = context.Sources
|
|
97
|
+
.Select(p => new Destination
|
|
98
|
+
{
|
|
99
|
+
Id = p.Id,
|
|
100
|
+
Name = p.Name,
|
|
101
|
+
// ...
|
|
102
|
+
}).ToList();
|
|
103
|
+
|
|
104
|
+
// Mapster 寫法
|
|
105
|
+
var list = context.Sources
|
|
106
|
+
.ProjectToType<Destination>()
|
|
107
|
+
.ToList();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 7. 全域預設設定
|
|
113
|
+
|
|
114
|
+
在 `Startup` 中統一設定全域映射行為:
|
|
115
|
+
|
|
116
|
+
```csharp
|
|
117
|
+
TypeAdapterConfig.GlobalSettings.Default
|
|
118
|
+
.PreserveReference(true);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 8. Dictionary Key 變小寫問題
|
|
124
|
+
|
|
125
|
+
Mapster 映射後 `Dictionary` 的 Key 預設會變小寫,有兩種解決方式:
|
|
126
|
+
|
|
127
|
+
### 方式一:全域設定(推薦)
|
|
128
|
+
|
|
129
|
+
```csharp
|
|
130
|
+
TypeAdapterConfig.GlobalSettings.Default
|
|
131
|
+
.NameMatchingStrategy(NameMatchingStrategy.Exact);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 方式二:單獨設定該映射
|
|
135
|
+
|
|
136
|
+
```csharp
|
|
137
|
+
public class TestMapper : IRegister
|
|
138
|
+
{
|
|
139
|
+
public void Register(TypeAdapterConfig config)
|
|
140
|
+
{
|
|
141
|
+
config.ForType<TestDemo, TestDemo2>()
|
|
142
|
+
.AfterMapping(dest =>
|
|
143
|
+
{
|
|
144
|
+
dest.Dic = new Dictionary<string, string>(
|
|
145
|
+
dest.Dic,
|
|
146
|
+
StringComparer.OrdinalIgnoreCase);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 快速速查
|
|
155
|
+
|
|
156
|
+
| 方法 | 說明 |
|
|
157
|
+
|------|------|
|
|
158
|
+
| `entity.Adapt<Dto>()` | 物件轉換(最常用) |
|
|
159
|
+
| `_mapper.Map<Dto>(entity)` | DI 注入方式轉換 |
|
|
160
|
+
| `query.ProjectToType<Dto>()` | EF Core Linq 投影轉換 |
|
|
161
|
+
| `IRegister` | 自訂映射規則介面 |
|
|
162
|
+
| `TypeAdapterConfig.GlobalSettings.Default` | 全域設定入口 |
|