| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 1 | 14 | 6 | 5% |
| Commands | 0 | 2 | 0 | 0% |
| Section tags | 0 | 3 | 1 | 0% |
What each file covers
Sections
1 shared · 14 only in A · 6 only in B- − Copilot instructions
- − General recommendations for working with Aspire
- − Running the application
- − Checking resources
- − Listing integrations
- − Debugging issues
- − Other Aspire MCP tools
- − Playwright MCP server
- − Updating the app host
- − Persistent containers
- − Aspire workload
- − Official documentation
- − BMAD AI 工作流
- − 与 CodeSpirit 规范集成
- + Scrutor 自动注册扩展方法
- + IScopedDependency(作用域 - 最常用)
- + ISingletonDependency(单例)
- + ITransientDependency(瞬时)
- + API 配置类中的服务注册
- + 自动注册(BaseApiConfiguration 已处理)
- 获取帮助
Commands
0 shared · 2 only in A · 0 only in B- − dotnet tool install --global dotnet-outdated-tool
- − dotnet-outdated
Section tags
0 shared · 3 only in A · 1 only in B- − code-style
- − agent-behaviour
- − docs
- + api
Line diff
xin-lai/CodeSpirit · AGENTS.md
@@ −1 @@
1# Copilot instructions
2
3This repository is set up to use Aspire. Aspire is an orchestrator for the entire application and will take care of configuring dependencies, building, and running the application. The resources that make up the application are defined in `apphost.cs` including application code and external dependencies.
4
5## General recommendations for working with Aspire
61. Before making any changes always run the apphost using `aspire run` and inspect the state of resources to make sure you are building from a known state.
71. Changes to the _apphost.cs_ file will require a restart of the application to take effect.
82. Make changes incrementally and run the aspire application using the `aspire run` command to validate changes.
93. Use the Aspire MCP tools to check the status of resources and debug issues.
10
11## Running the application
12To run the application run the following command:
13
14```
15aspire run
16```
17
18If there is already an instance of the application running it will prompt to stop the existing instance. You only need to restart the application if code in `apphost.cs` is changed, but if you experience problems it can be useful to reset everything to the starting state.
19
20## Checking resources
21To check the status of resources defined in the app model use the _list resources_ tool. This will show you the current state of each resource and if there are any issues. If a resource is not running as expected you can use the _execute resource command_ tool to restart it or perform other actions.
22
23## Listing integrations
24IMPORTANT! When a user asks you to add a resource to the app model you should first use the _list integrations_ tool to get a list of the current versions of all the available integrations. You should try to use the version of the integration which aligns with the version of the Aspire.AppHost.Sdk. Some integration versions may have a preview suffix. Once you have identified the correct integration you should always use the _get integration docs_ tool to fetch the latest documentation for the integration and follow the links to get additional guidance.
25
26## Debugging issues
27IMPORTANT! Aspire is designed to capture rich logs and telemetry for all resources defined in the app model. Use the following diagnostic tools when debugging issues with the application before making changes to make sure you are focusing on the right things.
28
291. _list structured logs_; use this tool to get details about structured logs.
302. _list console logs_; use this tool to get details about console logs.
313. _list traces_; use this tool to get details about traces.
324. _list trace structured logs_; use this tool to get logs related to a trace
33
34## Other Aspire MCP tools
35
361. _select apphost_; use this tool if working with multiple app hosts within a workspace.
372. _list apphosts_; use this tool to get details about active app hosts.
38
39## Playwright MCP server
40
41The playwright MCP server has also been configured in this repository and you should use it to perform functional investigations of the resources defined in the app model as you work on the codebase. To get endpoints that can be used for navigation using the playwright MCP server use the list resources tool.
42
43## Updating the app host
44The user may request that you update the Aspire apphost. You can do this using the `aspire update` command. This will update the apphost to the latest version and some of the Aspire specific packages in referenced projects, however you may need to manually update other packages in the solution to ensure compatibility. You can consider using the `dotnet-outdated` with the users consent. To install the `dotnet-outdated` tool use the following command:
45
46```
47dotnet tool install --global dotnet-outdated-tool
48```
49
50## Persistent containers
51IMPORTANT! Consider avoiding persistent containers early during development to avoid creating state management issues when restarting the app.
52
53## Aspire workload
54IMPORTANT! The aspire workload is obsolete. You should never attempt to install or use the Aspire workload.
55
56## Official documentation
57IMPORTANT! Always prefer official documentation when available. The following sites contain the official documentation for Aspire and related components
58
591. https://aspire.dev
602. https://learn.microsoft.com/dotnet/aspire
613. https://nuget.org (for specific integration package details)
62
63## BMAD AI 工作流
64
65本项目已集成 BMAD (Breakthrough Method of Agile AI-Driven Development) 完整工作流,用于结构化的软件开发生命周期管理。
66
67### 快速开始
68
691. **小型任务/Bug 修复** (Quick Flow):
70 - `/quick-spec` - 创建技术规范
71 - `/quick-dev` - 实现变更
72 - `/code-review` - 代码审查
73
742. **完整功能开发** (Full Flow):
75 - `/product-brief` - 产品需求简报
76 - `/create-prd` - 创建 PRD
77 - `/create-architecture` - 架构设计
78 - `/create-epics-and-stories` - 拆分为 Epic 和 Story
79 - `/sprint-planning` - Sprint 规划
80 - `/dev-story` - 实现 Story
81 - `/code-review` - 代码审查
82 - `/retrospective` - 复盘
83
84### 与 CodeSpirit 规范集成
85
86BMAD 工作流已配置为自动遵循 CodeSpirit 的所有开发规范(位于 `.cursor/rules/`)。在使用 BMAD 时:
87
88- PRD 会自动考虑多租户、多数据库、AI 功能等项目特性
89- 架构设计会遵循依赖注入、缓存策略等规范
90- Story 实现会应用正确的命名约定、DTO 设计、控制器规范等
91- 代码审查会执行 CodeSpirit 特定的审查清单
92
93### 获取帮助
94
95任何时候,输入 `/bmad-help` 可获取上下文相关的指导。
96
97详细使用指南请参考:
98- **[BMAD 使用教程](Docs/bmad/bmad-tutorial.md)** - 完整的综合教程(推荐新手阅读)
99- [BMAD 工作流指南](Docs/bmad/bmad-workflow-guide.md) - 详细的工作流使用指南
100- [BMAD 团队培训指南](Docs/bmad/bmad-team-guide.md) - 团队培训材料
101- [BMAD 集成技能](.cursor/skills/bmad-integration/SKILL.md) - BMAD 与 CodeSpirit 集成
102- [项目上下文文档](project-context.md) - 项目上下文和规范引用
xin-lai/CodeSpirit · .cursor/rules/dependency-injection.mdc
@@ +1 @@
1---
2description: CodeSpirit 依赖注入规范 - Scrutor自动注册、生命周期管理
3globs:
4 - "*Service.cs"
5 - "**/Services/**/*.cs"
6 - "**/*Seeder*.cs"
7 - "**/*Helper*.cs"
8 - "**/*Handler*.cs"
9 - "**/*Repository*.cs"
10alwaysApply: false
11---
12
13# 依赖注入规范(Scrutor 自动注册)
14
15## 概述
16
17项目使用 Scrutor 库实现基于标记接口的自动依赖注入,无需手动注册服务。
18
19## 标记接口
20
21位于 `CodeSpirit.Core.DependencyInjection` 命名空间:
22
23| 接口 | 生命周期 | 适用场景 |
24|-----|---------|---------|
25| `IScopedDependency` | Scoped | 业务服务、数据库操作、请求相关 |
26| `ITransientDependency` | Transient | 无状态工具类、轻量操作 |
27| `ISingletonDependency` | Singleton | 配置服务、缓存、ID生成器 |
28
29### 生命周期说明
30
31```csharp
32// IScopedDependency - 作用域注入
33// 同一个请求中是同一个实例,不同请求是不同实例
34// 推荐:大多数业务服务、DbContext 相关操作
35
36// ITransientDependency - 瞬时注入
37// 每次注入都创建新实例
38// 推荐:无状态工具类、不持有资源的服务
39
40// ISingletonDependency - 单例注入
41// 整个应用生命周期只有一个实例
42// 推荐:配置服务、缓存管理、ID生成器
43```
44
45## 标记方式
46
47### 方式一:接口继承标记接口(推荐)
48
49接口继承标记接口,实现类无需再次标记:
50
51```csharp
52// 接口定义 - 继承 IScopedDependency
53public interface IAuthService : IScopedDependency
54{
55 Task<AuthResultDto> LoginAsync(LoginDto input);
56 Task<bool> LogoutAsync(long userId);
57}
58
59// 实现类 - 无需标记接口
60public class AuthService : IAuthService
61{
62 private readonly IRepository<User> _userRepository;
63
64 public AuthService(IRepository<User> userRepository)
65 {
66 _userRepository = userRepository;
67 }
68
69 public async Task<AuthResultDto> LoginAsync(LoginDto input)
70 {
71 // 实现逻辑
72 }
73}
74```
75
76### 方式二:实现类标记接口
77
78适用于无业务接口的服务类:
79
80```csharp
81// 无接口的服务类,直接实现标记接口
82public class SeederService : IScopedDependency
83{
84 private readonly IServiceProvider _serviceProvider;
85 private readonly ILogger<SeederService> _logger;
86
87 public SeederService(IServiceProvider serviceProvider, ILogger<SeederService> logger)
88 {
89 _serviceProvider = serviceProvider;
90 _logger = logger;
91 }
92
93 public async Task SeedAsync()
94 {
95 // 初始化种子数据
96 }
97}
98```
99
100### 方式三:同时实现业务接口和标记接口
101
102适用于需要明确指定生命周期的服务:
103
104```csharp
105public interface IUserService : IBaseCRUDService<User, long, CreateUserDto, UpdateUserDto, UserQueryDto>
106{
107 Task<UserDto> GetByUsernameAsync(string username);
108}
109
110public class UserService : BaseCRUDService<User, long, CreateUserDto, UpdateUserDto, UserQueryDto>,
111 IUserService, IScopedDependency
112{
113 public async Task<UserDto> GetByUsernameAsync(string username)
114 {
115 // 实现逻辑
116 }
117}
118```
119
120## 生命周期选择指南
121
122### IScopedDependency(作用域 - 最常用)
123
124```csharp
125// ✅ 业务服务
126public interface IQuestionService : IScopedDependency
127{
128 Task<QuestionDto> GetByIdAsync(long id);
129 Task CreateAsync(CreateQuestionDto dto);
130}
131
132// ✅ 数据访问服务
133public interface IExamRepository : IScopedDependency
134{
135 Task<Exam> GetWithQuestionsAsync(long examId);
136}
137
138// ✅ 种子数据服务
139public class TenantSeeder : IScopedDependency
140{
141 public async Task SeedAsync() { }
142}
143```
144
145### ISingletonDependency(单例)
146
147```csharp
148// ✅ ID 生成器
149public interface IIdGenerator : ISingletonDependency
150{
151 long NewId();
152}
153
154// ✅ 缓存服务
155public interface IConfigCacheService : ISingletonDependency
156{
157 Task<string> GetAsync(string key);
158 Task SetAsync(string key, string value, TimeSpan? expiry = null);
159}
160
161// ✅ 端点扫描器(应用启动时扫描一次)
162public class AiFormFillEndpointScanner : ISingletonDependency
163{
164 public void ScanAssemblies(params Assembly[] assemblies) { }
165}
166
167// ✅ 本地化设置初始化器
168public class LocalizationSettingsInitializer : ISingletonDependency
169{
170 public void Initialize() { }
171}
172```
173
174### ITransientDependency(瞬时)
175
176```csharp
177// ✅ 无状态工具类
178public interface IPasswordHasher : ITransientDependency
179{
180 string HashPassword(string password);
181 bool VerifyPassword(string password, string hash);
182}
183
184// ✅ 存储提供器工厂(每次创建新实例)
185public interface IStorageProviderFactory : ITransientDependency
186{
187 IStorageProvider CreateProvider(string providerType);
188}
189
190// ✅ 配置变更通知器
191public interface IConfigChangeNotifier : ITransientDependency
192{
193 Task NotifyChangeAsync(string configKey);
194}
195```
196
197## Scrutor 自动注册扩展方法
198
199### 基础注册方法
200
201```csharp
202// 位于 CodeSpirit.Shared.DependencyInjection.ServiceCollectionExtensions
203
204// 自动扫描并注册标记接口的服务
205services.AddDependencyInjectionWithScrutor(Assembly.GetExecutingAssembly());
206
207// 可同时扫描多个程序集
208services.AddDependencyInjectionWithScrutor(
209 Assembly.GetExecutingAssembly(),
210 typeof(SharedService).Assembly);
211```
212
213### 高级注册方法
214
215```csharp
216// 按命名约定自动注册(Service、Repository 后缀)
217services.AddAdvancedDependencyInjection(Assembly.GetExecutingAssembly());
218```
219
220### 装饰器模式
221
222```csharp
223// 使用装饰器包装现有服务
224services.AddDecorator<IUserService, CachingUserServiceDecorator>();
225services.AddDecorator<ILogger<UserService>, AuditLoggerDecorator<UserService>>();
226```
227
228## 注册行为
229
230Scrutor 自动完成以下注册:
231
2321. **接口注册**:服务注册为其实现的业务接口
2332. **自身注册**:服务同时注册为自身类型(可直接注入具体类)
234
235```csharp
236// 给定服务类
237public class UserService : IUserService, IScopedDependency { }
238
239// Scrutor 自动注册:
240// services.AddScoped<IUserService, UserService>(); // 接口注册
241// services.AddScoped<UserService>(); // 自身注册
242
243// 两种方式都可以注入:
244public class UserController
245{
246 public UserController(
247 IUserService userService, // ✅ 接口注入
248 UserService userServiceImpl) // ✅ 具体类注入
249 { }
250}
251```
252
253## API 配置类中的服务注册
254
255### 自动注册(BaseApiConfiguration 已处理)
256
257```csharp
258public class ExamApiConfiguration : BaseApiConfiguration
259{
260 public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
261 {
262 base.ConfigureServices(services, configuration);
263
264 // Scrutor 自动注册已在 BaseApiConfiguration 中完成
265 // 无需再调用 AddDependencyInjectionWithScrutor
266 }
267}
268```
269
270### 手动注册特殊服务
271
272```csharp
273public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
274{
275 base.ConfigureServices(services, configuration);
276
277 // 手动注册:特殊配置、外部库服务、条件注册
278 services.AddScoped<ISpecialService>(sp =>
279 new SpecialService(sp.GetRequiredService<IOptions<SpecialOptions>>()));
280
281 // 注册外部库服务
282 services.AddHttpClient<IExternalApiClient, ExternalApiClient>();
283
284 // 条件注册
285 if (configuration.GetValue<bool>("Features:EnableNewFeature"))
286 {
287 services.AddScoped<INewFeatureService, NewFeatureService>();
288 }
289}
290```
291
292## 依赖注入最佳实践
293
294### ✅ 推荐做法
295
296```csharp
297// 1. 构造函数注入(推荐)
298public class QuestionService : IQuestionService, IScopedDependency
299{
300 private readonly IRepository<Question> _repository;
301 private readonly IMapper _mapper;
302
303 public QuestionService(IRepository<Question> repository, IMapper mapper)
304 {
305 _repository = repository;
306 _mapper = mapper;
307 }
308}
309
310// 2. 接口定义继承标记接口
311public interface IExamService : IScopedDependency
312{
313 Task<ExamDto> GetByIdAsync(long id);
314}
315
316// 3. 使用 IServiceProvider 延迟解析(避免循环依赖)
317public class CrudDialogHandler : IScopedDependency
318{
319 private readonly IServiceProvider _serviceProvider;
320
321 public CrudDialogHandler(IServiceProvider serviceProvider)
322 {
323 _serviceProvider = serviceProvider;
324 }
325
326 private ColumnHelper ColumnHelper => _serviceProvider.GetRequiredService<ColumnHelper>();
327}
328```
329
330### ❌ 禁止做法
331
332```csharp
333// 1. 不要手动注册标记接口的服务(会重复注册)
334services.AddScoped<IUserService, UserService>(); // ❌ Scrutor 已自动注册
335
336// 2. 不要使用服务定位器反模式
337public class BadService
338{
339 public void DoWork()
340 {
341 var service = ServiceLocator.GetService<IOtherService>(); // ❌
342 }
343}
344
345// 3. 不要在 Singleton 服务中注入 Scoped 服务
346public class BadSingletonService : ISingletonDependency
347{
348 private readonly IUserService _userService; // ❌ Scoped 服务不能注入到 Singleton
349}
350
351// 4. 不要创建多余的包装接口
352public interface IUserServiceWrapper : IUserService { } // ❌ 不必要
353```
354
355## 常见问题
356
357### 循环依赖处理
358
359```csharp
360// 使用 IServiceProvider 延迟解析
361public class ServiceA : IScopedDependency
362{
363 private readonly IServiceProvider _serviceProvider;
364
365 public ServiceA(IServiceProvider serviceProvider)
366 {
367 _serviceProvider = serviceProvider;
368 }
369
370 // 延迟获取依赖
371 private IServiceB ServiceB => _serviceProvider.GetRequiredService<IServiceB>();
372}
373```
374
375### 条件服务注册
376
377```csharp
378// 在 API 配置类中进行条件注册
379public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
380{
381 base.ConfigureServices(services, configuration);
382
383 var storageType = configuration["Storage:Type"];
384 services.AddScoped<IStorageService>(sp => storageType switch
385 {
386 "S3" => sp.GetRequiredService<S3StorageService>(),
387 "Azure" => sp.GetRequiredService<AzureBlobStorageService>(),
388 _ => sp.GetRequiredService<LocalStorageService>()
389 });
390}
391```
392
393### 多实现注册
394
395```csharp
396// 注册多个实现
397services.AddScoped<INotificationService, EmailNotificationService>();
398services.AddScoped<INotificationService, SmsNotificationService>();
399
400// 注入所有实现
401public class NotificationManager
402{
403 private readonly IEnumerable<INotificationService> _notificationServices;
404
405 public NotificationManager(IEnumerable<INotificationService> notificationServices)
406 {
407 _notificationServices = notificationServices;
408 }
409
410 public async Task NotifyAllAsync(string message)
411 {
412 foreach (var service in _notificationServices)
413 {
414 await service.SendAsync(message);
415 }
416 }
417}
418```
419
420## 命名空间引用
421
422```csharp
423using CodeSpirit.Core.DependencyInjection; // 标记接口
424using CodeSpirit.Shared.DependencyInjection; // 扩展方法
425```
426
@@ −1 +1 @@
1−# Copilot instructions
1+---
2+description: CodeSpirit 依赖注入规范 - Scrutor自动注册、生命周期管理
3+globs:
4+ - "*Service.cs"
5+ - "**/Services/**/*.cs"
6+ - "**/*Seeder*.cs"
7+ - "**/*Helper*.cs"
8+ - "**/*Handler*.cs"
9+ - "**/*Repository*.cs"
10+alwaysApply: false
11+---
212
3−This repository is set up to use Aspire. Aspire is an orchestrator for the entire application and will take care of configuring dependencies, building, and running the application. The resources that make up the application are defined in `apphost.cs` including application code and external dependencies.
13+# 依赖注入规范(Scrutor 自动注册)
414
5−## General recommendations for working with Aspire
6−1. Before making any changes always run the apphost using `aspire run` and inspect the state of resources to make sure you are building from a known state.
7−1. Changes to the _apphost.cs_ file will require a restart of the application to take effect.
8−2. Make changes incrementally and run the aspire application using the `aspire run` command to validate changes.
9−3. Use the Aspire MCP tools to check the status of resources and debug issues.
15+## 概述
1016
11−## Running the application
12−To run the application run the following command:
17+项目使用 Scrutor 库实现基于标记接口的自动依赖注入,无需手动注册服务。
1318
19+## 标记接口
20+
21+位于 `CodeSpirit.Core.DependencyInjection` 命名空间:
22+
23+| 接口 | 生命周期 | 适用场景 |
24+|-----|---------|---------|
25+| `IScopedDependency` | Scoped | 业务服务、数据库操作、请求相关 |
26+| `ITransientDependency` | Transient | 无状态工具类、轻量操作 |
27+| `ISingletonDependency` | Singleton | 配置服务、缓存、ID生成器 |
28+
29+### 生命周期说明
30+
31+```csharp
32+// IScopedDependency - 作用域注入
33+// 同一个请求中是同一个实例,不同请求是不同实例
34+// 推荐:大多数业务服务、DbContext 相关操作
35+
36+// ITransientDependency - 瞬时注入
37+// 每次注入都创建新实例
38+// 推荐:无状态工具类、不持有资源的服务
39+
40+// ISingletonDependency - 单例注入
41+// 整个应用生命周期只有一个实例
42+// 推荐:配置服务、缓存管理、ID生成器
1443 ```
15−aspire run
44+
45+## 标记方式
46+
47+### 方式一:接口继承标记接口(推荐)
48+
49+接口继承标记接口,实现类无需再次标记:
50+
51+```csharp
52+// 接口定义 - 继承 IScopedDependency
53+public interface IAuthService : IScopedDependency
54+{
55+ Task<AuthResultDto> LoginAsync(LoginDto input);
56+ Task<bool> LogoutAsync(long userId);
57+}
58+
59+// 实现类 - 无需标记接口
60+public class AuthService : IAuthService
61+{
62+ private readonly IRepository<User> _userRepository;
63+
64+ public AuthService(IRepository<User> userRepository)
65+ {
66+ _userRepository = userRepository;
67+ }
68+
69+ public async Task<AuthResultDto> LoginAsync(LoginDto input)
70+ {
71+ // 实现逻辑
72+ }
73+}
1674 ```
1775
18−If there is already an instance of the application running it will prompt to stop the existing instance. You only need to restart the application if code in `apphost.cs` is changed, but if you experience problems it can be useful to reset everything to the starting state.
76+### 方式二:实现类标记接口
1977
20−## Checking resources
21−To check the status of resources defined in the app model use the _list resources_ tool. This will show you the current state of each resource and if there are any issues. If a resource is not running as expected you can use the _execute resource command_ tool to restart it or perform other actions.
78+适用于无业务接口的服务类:
2279
23−## Listing integrations
24−IMPORTANT! When a user asks you to add a resource to the app model you should first use the _list integrations_ tool to get a list of the current versions of all the available integrations. You should try to use the version of the integration which aligns with the version of the Aspire.AppHost.Sdk. Some integration versions may have a preview suffix. Once you have identified the correct integration you should always use the _get integration docs_ tool to fetch the latest documentation for the integration and follow the links to get additional guidance.
80+```csharp
81+// 无接口的服务类,直接实现标记接口
82+public class SeederService : IScopedDependency
83+{
84+ private readonly IServiceProvider _serviceProvider;
85+ private readonly ILogger<SeederService> _logger;
2586
26−## Debugging issues
27−IMPORTANT! Aspire is designed to capture rich logs and telemetry for all resources defined in the app model. Use the following diagnostic tools when debugging issues with the application before making changes to make sure you are focusing on the right things.
87+ public SeederService(IServiceProvider serviceProvider, ILogger<SeederService> logger)
88+ {
89+ _serviceProvider = serviceProvider;
90+ _logger = logger;
91+ }
2892
29−1. _list structured logs_; use this tool to get details about structured logs.
30−2. _list console logs_; use this tool to get details about console logs.
31−3. _list traces_; use this tool to get details about traces.
32−4. _list trace structured logs_; use this tool to get logs related to a trace
93+ public async Task SeedAsync()
94+ {
95+ // 初始化种子数据
96+ }
97+}
98+```
3399
34−## Other Aspire MCP tools
100+### 方式三:同时实现业务接口和标记接口
35101
36−1. _select apphost_; use this tool if working with multiple app hosts within a workspace.
37−2. _list apphosts_; use this tool to get details about active app hosts.
102+适用于需要明确指定生命周期的服务:
38103
39−## Playwright MCP server
104+```csharp
105+public interface IUserService : IBaseCRUDService<User, long, CreateUserDto, UpdateUserDto, UserQueryDto>
106+{
107+ Task<UserDto> GetByUsernameAsync(string username);
108+}
40109
41−The playwright MCP server has also been configured in this repository and you should use it to perform functional investigations of the resources defined in the app model as you work on the codebase. To get endpoints that can be used for navigation using the playwright MCP server use the list resources tool.
110+public class UserService : BaseCRUDService<User, long, CreateUserDto, UpdateUserDto, UserQueryDto>,
111+ IUserService, IScopedDependency
112+{
113+ public async Task<UserDto> GetByUsernameAsync(string username)
114+ {
115+ // 实现逻辑
116+ }
117+}
118+```
42119
43−## Updating the app host
44−The user may request that you update the Aspire apphost. You can do this using the `aspire update` command. This will update the apphost to the latest version and some of the Aspire specific packages in referenced projects, however you may need to manually update other packages in the solution to ensure compatibility. You can consider using the `dotnet-outdated` with the users consent. To install the `dotnet-outdated` tool use the following command:
120+## 生命周期选择指南
45121
122+### IScopedDependency(作用域 - 最常用)
123+
124+```csharp
125+// ✅ 业务服务
126+public interface IQuestionService : IScopedDependency
127+{
128+ Task<QuestionDto> GetByIdAsync(long id);
129+ Task CreateAsync(CreateQuestionDto dto);
130+}
131+
132+// ✅ 数据访问服务
133+public interface IExamRepository : IScopedDependency
134+{
135+ Task<Exam> GetWithQuestionsAsync(long examId);
136+}
137+
138+// ✅ 种子数据服务
139+public class TenantSeeder : IScopedDependency
140+{
141+ public async Task SeedAsync() { }
142+}
46143 ```
47−dotnet tool install --global dotnet-outdated-tool
144+
145+### ISingletonDependency(单例)
146+
147+```csharp
148+// ✅ ID 生成器
149+public interface IIdGenerator : ISingletonDependency
150+{
151+ long NewId();
152+}
153+
154+// ✅ 缓存服务
155+public interface IConfigCacheService : ISingletonDependency
156+{
157+ Task<string> GetAsync(string key);
158+ Task SetAsync(string key, string value, TimeSpan? expiry = null);
159+}
160+
161+// ✅ 端点扫描器(应用启动时扫描一次)
162+public class AiFormFillEndpointScanner : ISingletonDependency
163+{
164+ public void ScanAssemblies(params Assembly[] assemblies) { }
165+}
166+
167+// ✅ 本地化设置初始化器
168+public class LocalizationSettingsInitializer : ISingletonDependency
169+{
170+ public void Initialize() { }
171+}
48172 ```
49173
50−## Persistent containers
51−IMPORTANT! Consider avoiding persistent containers early during development to avoid creating state management issues when restarting the app.
174+### ITransientDependency(瞬时)
52175
53−## Aspire workload
54−IMPORTANT! The aspire workload is obsolete. You should never attempt to install or use the Aspire workload.
176+```csharp
177+// ✅ 无状态工具类
178+public interface IPasswordHasher : ITransientDependency
179+{
180+ string HashPassword(string password);
181+ bool VerifyPassword(string password, string hash);
182+}
55183
56−## Official documentation
57−IMPORTANT! Always prefer official documentation when available. The following sites contain the official documentation for Aspire and related components
184+// ✅ 存储提供器工厂(每次创建新实例)
185+public interface IStorageProviderFactory : ITransientDependency
186+{
187+ IStorageProvider CreateProvider(string providerType);
188+}
58189
59−1. https://aspire.dev
60−2. https://learn.microsoft.com/dotnet/aspire
61−3. https://nuget.org (for specific integration package details)
190+// ✅ 配置变更通知器
191+public interface IConfigChangeNotifier : ITransientDependency
192+{
193+ Task NotifyChangeAsync(string configKey);
194+}
195+```
62196
63−## BMAD AI 工作流
197+## Scrutor 自动注册扩展方法
64198
65−本项目已集成 BMAD (Breakthrough Method of Agile AI-Driven Development) 完整工作流,用于结构化的软件开发生命周期管理。
199+### 基础注册方法
66200
67−### 快速开始
201+```csharp
202+// 位于 CodeSpirit.Shared.DependencyInjection.ServiceCollectionExtensions
68203
69−1. **小型任务/Bug 修复** (Quick Flow):
70− - `/quick-spec` - 创建技术规范
71− - `/quick-dev` - 实现变更
72− - `/code-review` - 代码审查
204+// 自动扫描并注册标记接口的服务
205+services.AddDependencyInjectionWithScrutor(Assembly.GetExecutingAssembly());
73206
74−2. **完整功能开发** (Full Flow):
75− - `/product-brief` - 产品需求简报
76− - `/create-prd` - 创建 PRD
77− - `/create-architecture` - 架构设计
78− - `/create-epics-and-stories` - 拆分为 Epic 和 Story
79− - `/sprint-planning` - Sprint 规划
80− - `/dev-story` - 实现 Story
81− - `/code-review` - 代码审查
82− - `/retrospective` - 复盘
207+// 可同时扫描多个程序集
208+services.AddDependencyInjectionWithScrutor(
209+ Assembly.GetExecutingAssembly(),
210+ typeof(SharedService).Assembly);
211+```
83212
84−### 与 CodeSpirit 规范集成
213+### 高级注册方法
85214
86−BMAD 工作流已配置为自动遵循 CodeSpirit 的所有开发规范(位于 `.cursor/rules/`)。在使用 BMAD 时:
215+```csharp
216+// 按命名约定自动注册(Service、Repository 后缀)
217+services.AddAdvancedDependencyInjection(Assembly.GetExecutingAssembly());
218+```
87219
88−- PRD 会自动考虑多租户、多数据库、AI 功能等项目特性
89−- 架构设计会遵循依赖注入、缓存策略等规范
90−- Story 实现会应用正确的命名约定、DTO 设计、控制器规范等
91−- 代码审查会执行 CodeSpirit 特定的审查清单
220+### 装饰器模式
92221
93−### 获取帮助
222+```csharp
223+// 使用装饰器包装现有服务
224+services.AddDecorator<IUserService, CachingUserServiceDecorator>();
225+services.AddDecorator<ILogger<UserService>, AuditLoggerDecorator<UserService>>();
226+```
94227
95−任何时候,输入 `/bmad-help` 可获取上下文相关的指导。
228+## 注册行为
96229
97−详细使用指南请参考:
98−- **[BMAD 使用教程](Docs/bmad/bmad-tutorial.md)** - 完整的综合教程(推荐新手阅读)
99−- [BMAD 工作流指南](Docs/bmad/bmad-workflow-guide.md) - 详细的工作流使用指南
100−- [BMAD 团队培训指南](Docs/bmad/bmad-team-guide.md) - 团队培训材料
101−- [BMAD 集成技能](.cursor/skills/bmad-integration/SKILL.md) - BMAD 与 CodeSpirit 集成
102−- [项目上下文文档](project-context.md) - 项目上下文和规范引用
230+Scrutor 自动完成以下注册:
231+
232+1. **接口注册**:服务注册为其实现的业务接口
233+2. **自身注册**:服务同时注册为自身类型(可直接注入具体类)
234+
235+```csharp
236+// 给定服务类
237+public class UserService : IUserService, IScopedDependency { }
238+
239+// Scrutor 自动注册:
240+// services.AddScoped<IUserService, UserService>(); // 接口注册
241+// services.AddScoped<UserService>(); // 自身注册
242+
243+// 两种方式都可以注入:
244+public class UserController
245+{
246+ public UserController(
247+ IUserService userService, // ✅ 接口注入
248+ UserService userServiceImpl) // ✅ 具体类注入
249+ { }
250+}
251+```
252+
253+## API 配置类中的服务注册
254+
255+### 自动注册(BaseApiConfiguration 已处理)
256+
257+```csharp
258+public class ExamApiConfiguration : BaseApiConfiguration
259+{
260+ public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
261+ {
262+ base.ConfigureServices(services, configuration);
263+
264+ // Scrutor 自动注册已在 BaseApiConfiguration 中完成
265+ // 无需再调用 AddDependencyInjectionWithScrutor
266+ }
267+}
268+```
269+
270+### 手动注册特殊服务
271+
272+```csharp
273+public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
274+{
275+ base.ConfigureServices(services, configuration);
276+
277+ // 手动注册:特殊配置、外部库服务、条件注册
278+ services.AddScoped<ISpecialService>(sp =>
279+ new SpecialService(sp.GetRequiredService<IOptions<SpecialOptions>>()));
280+
281+ // 注册外部库服务
282+ services.AddHttpClient<IExternalApiClient, ExternalApiClient>();
283+
284+ // 条件注册
285+ if (configuration.GetValue<bool>("Features:EnableNewFeature"))
286+ {
287+ services.AddScoped<INewFeatureService, NewFeatureService>();
288+ }
289+}
290+```
291+
292+## 依赖注入最佳实践
293+
294+### ✅ 推荐做法
295+
296+```csharp
297+// 1. 构造函数注入(推荐)
298+public class QuestionService : IQuestionService, IScopedDependency
299+{
300+ private readonly IRepository<Question> _repository;
301+ private readonly IMapper _mapper;
302+
303+ public QuestionService(IRepository<Question> repository, IMapper mapper)
304+ {
305+ _repository = repository;
306+ _mapper = mapper;
307+ }
308+}
309+
310+// 2. 接口定义继承标记接口
311+public interface IExamService : IScopedDependency
312+{
313+ Task<ExamDto> GetByIdAsync(long id);
314+}
315+
316+// 3. 使用 IServiceProvider 延迟解析(避免循环依赖)
317+public class CrudDialogHandler : IScopedDependency
318+{
319+ private readonly IServiceProvider _serviceProvider;
320+
321+ public CrudDialogHandler(IServiceProvider serviceProvider)
322+ {
323+ _serviceProvider = serviceProvider;
324+ }
325+
326+ private ColumnHelper ColumnHelper => _serviceProvider.GetRequiredService<ColumnHelper>();
327+}
328+```
329+
330+### ❌ 禁止做法
331+
332+```csharp
333+// 1. 不要手动注册标记接口的服务(会重复注册)
334+services.AddScoped<IUserService, UserService>(); // ❌ Scrutor 已自动注册
335+
336+// 2. 不要使用服务定位器反模式
337+public class BadService
338+{
339+ public void DoWork()
340+ {
341+ var service = ServiceLocator.GetService<IOtherService>(); // ❌
342+ }
343+}
344+
345+// 3. 不要在 Singleton 服务中注入 Scoped 服务
346+public class BadSingletonService : ISingletonDependency
347+{
348+ private readonly IUserService _userService; // ❌ Scoped 服务不能注入到 Singleton
349+}
350+
351+// 4. 不要创建多余的包装接口
352+public interface IUserServiceWrapper : IUserService { } // ❌ 不必要
353+```
354+
355+## 常见问题
356+
357+### 循环依赖处理
358+
359+```csharp
360+// 使用 IServiceProvider 延迟解析
361+public class ServiceA : IScopedDependency
362+{
363+ private readonly IServiceProvider _serviceProvider;
364+
365+ public ServiceA(IServiceProvider serviceProvider)
366+ {
367+ _serviceProvider = serviceProvider;
368+ }
369+
370+ // 延迟获取依赖
371+ private IServiceB ServiceB => _serviceProvider.GetRequiredService<IServiceB>();
372+}
373+```
374+
375+### 条件服务注册
376+
377+```csharp
378+// 在 API 配置类中进行条件注册
379+public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
380+{
381+ base.ConfigureServices(services, configuration);
382+
383+ var storageType = configuration["Storage:Type"];
384+ services.AddScoped<IStorageService>(sp => storageType switch
385+ {
386+ "S3" => sp.GetRequiredService<S3StorageService>(),
387+ "Azure" => sp.GetRequiredService<AzureBlobStorageService>(),
388+ _ => sp.GetRequiredService<LocalStorageService>()
389+ });
390+}
391+```
392+
393+### 多实现注册
394+
395+```csharp
396+// 注册多个实现
397+services.AddScoped<INotificationService, EmailNotificationService>();
398+services.AddScoped<INotificationService, SmsNotificationService>();
399+
400+// 注入所有实现
401+public class NotificationManager
402+{
403+ private readonly IEnumerable<INotificationService> _notificationServices;
404+
405+ public NotificationManager(IEnumerable<INotificationService> notificationServices)
406+ {
407+ _notificationServices = notificationServices;
408+ }
409+
410+ public async Task NotifyAllAsync(string message)
411+ {
412+ foreach (var service in _notificationServices)
413+ {
414+ await service.SendAsync(message);
415+ }
416+ }
417+}
418+```
419+
420+## 命名空间引用
421+
422+```csharp
423+using CodeSpirit.Core.DependencyInjection; // 标记接口
424+using CodeSpirit.Shared.DependencyInjection; // 扩展方法
425+```
426+
