# .NET 6.0 开发规范 (基于ASP.NET Boilerplate) ## 📁 项目结构 ``` YunDa.SOMS/ ├── YunDa.Application/ │ ├── YunDa.SOMS.Application/ # API服务层 │ └── YunDa.SOMS.DataTransferObject/ # DTO中间层 ├── YunDa.Domain/ # 实体与业务逻辑 ├── YunDa.Web/ │ └── YunDa.SOMS.Web.MVC/ # MVC Web项目 └── YunDa.Infrastructure/ # 基础设施层 ``` ## 🏗️ 架构规范 ### 分层职责 | 层级 | 职责 | 依赖规则 | |------|------|----------| | **Application** | 继承`SOMSAppServiceBase`,处理DTO转换、业务协调、API接口 | 可依赖Domain | | **Domain** | 纯业务逻辑、实体定义、领域服务 | 不依赖任何外部设施 | | **Infrastructure** | 数据访问、外部服务调用、技术实现 | 实现Domain接口 | | **Web.MVC** | 前端页面、控制器、视图 | 依赖Application | ### 数据库配置 - **MySQL**: 配置数据存储 - **MongoDB**: 记录数据存储 - **Redis**: 数据共享与缓存 ### 服务注册 ```csharp // AppService/DomainService: Scoped生命周期 // 使用ABP约定自动注册 [Dependency(ReplaceServices = true)] public class UserManagementAppService : SOMSAppServiceBase, IUserManagementAppService { // 自动注册为Scoped } ``` ## 📝 代码规范 ### 命名约定 ```csharp // ✅ 类名: PascalCase public class UserManagementAppService : SOMSAppServiceBase // ✅ 接口: I + PascalCase public interface IUserManagementAppService : IApplicationService // ✅ 方法: 动词开头 + Async (异步方法) public async Task> CreateUserAsync(CreateUserInput input) // ✅ 变量/字段: camelCase,私有字段下划线前缀 private readonly IUserRepository _userRepository; private readonly ILogger _logger; public string userName { get; set; } // ✅ 常量: UPPER_SNAKE_CASE private const string DEFAULT_USER_ROLE = "User"; // ✅ 枚举: PascalCase public enum UserStatus { Active = 0, Inactive = 1, Suspended = 2 } ``` ### 前后端命名差异 ```csharp // 后端 (PascalCase) public class UserCreateInput { public string UserName { get; set; } public string EmailAddress { get; set; } public DateTime CreatedTime { get; set; } } // 前端请求 (camelCase) { "userName": "john_doe", "emailAddress": "john@example.com", "createdTime": "2024-01-01T00:00:00Z" } ``` ### 异步编程规范 ```csharp // ✅ 正确的异步方法 public async Task> GetUserAsync( Guid userId, CancellationToken cancellationToken = default) { var user = await _userRepository .GetAsync(userId, cancellationToken) .ConfigureAwait(false); return new RequestResult { Flag = true, ResultData = ObjectMapper.Map(user) }; } // ❌ 避免的写法 public RequestResult GetUser(Guid userId) // 缺少async { var user = _userRepository.Get(userId); // 同步调用 // ... } ``` ### C# 8.0+ 特性使用 ```csharp // ✅ Nullable引用类型 public async Task> GetOptionalDataAsync(Guid? id) { if (id is null) return RequestResult.Failed("ID不能为空"); // ... } // ✅ Switch表达式 public string GetStatusDescription(UserStatus status) => status switch { UserStatus.Active => "正常", UserStatus.Inactive => "未激活", UserStatus.Suspended => "已暂停", _ => "未知状态" }; // ✅ Pattern matching public bool IsValidUser(object user) => user switch { User { IsActive: true, EmailConfirmed: true } => true, _ => false }; ``` ## 🛡️ AppService标准模板 ### 基础模板 ```csharp [AbpAuthorize("Permission.User.Management")] public class UserManagementAppService : SOMSAppServiceBase, IUserManagementAppService { private readonly IUserRepository _userRepository; private readonly IUserDomainService _userDomainService; public UserManagementAppService( IUserRepository userRepository, IUserDomainService userDomainService) { _userRepository = userRepository; _userDomainService = userDomainService; } public async Task> CreateUserAsync( CreateUserInput input, CancellationToken cancellationToken = default) { var result = new RequestResult(); try { // 1. 输入验证 await ValidateInputAsync(input); // 2. 业务逻辑处理 var user = await _userDomainService .CreateUserAsync(input.UserName, input.EmailAddress, cancellationToken) .ConfigureAwait(false); // 3. 持久化 await _userRepository.InsertAsync(user, cancellationToken).ConfigureAwait(false); await CurrentUnitOfWork.SaveChangesAsync(cancellationToken); // 4. 结果转换 result.Flag = true; result.ResultData = ObjectMapper.Map(user); result.Message = "用户创建成功"; Log4Helper.Info(GetType(), $"用户创建成功: {user.UserName}"); } catch (ValidationException ex) { result.Flag = false; result.Message = $"数据验证失败:{ex.Message}"; Log4Helper.Info(GetType(), "用户创建-验证异常", ex); } catch (BusinessException ex) { result.Flag = false; result.Message = ex.Message; Log4Helper.Warning(GetType(), "用户创建-业务异常", ex); } catch (Exception ex) { result.Flag = false; result.Message = "系统异常,请联系管理员"; Log4Helper.Error(GetType(), "用户创建-系统异常", ex); } return result; } private async Task ValidateInputAsync(CreateUserInput input) { if (string.IsNullOrWhiteSpace(input.UserName)) throw new ValidationException("用户名不能为空"); if (await _userRepository.AnyAsync(u => u.UserName == input.UserName)) throw new ValidationException("用户名已存在"); } } ``` ### CRUD标准模板 ```csharp public class StandardCrudAppService : SOMSAppServiceBase where TEntity : class, IEntity where TDto : IEntityDto { protected readonly IRepository Repository; protected StandardCrudAppService(IRepository repository) { Repository = repository; } public virtual async Task> GetAsync( TKey id, CancellationToken cancellationToken = default) { var result = new RequestResult(); try { var entity = await Repository .GetAsync(id, cancellationToken) .ConfigureAwait(false); result.Flag = true; result.ResultData = ObjectMapper.Map(entity); } catch (EntityNotFoundException ex) { result.Flag = false; result.Message = $"未找到ID为{id}的记录"; Log4Helper.Warning(GetType(), "记录不存在", ex); } catch (Exception ex) { result.Flag = false; result.Message = "获取数据失败"; Log4Helper.Error(GetType(), "获取数据异常", ex); } return result; } public virtual async Task>> GetPagedAsync( PagedAndSortedRequestDto input, CancellationToken cancellationToken = default) { var result = new RequestResult>(); try { var query = Repository.GetAll(); var totalCount = await query.CountAsync(cancellationToken).ConfigureAwait(false); var entities = await query .OrderBy(input.Sorting ?? "Id") .Skip(input.SkipCount) .Take(input.MaxResultCount) .ToListAsync(cancellationToken) .ConfigureAwait(false); result.Flag = true; result.ResultData = new PagedResultDto { TotalCount = totalCount, Items = ObjectMapper.Map>(entities) }; } catch (Exception ex) { result.Flag = false; result.Message = "获取分页数据失败"; Log4Helper.Error(GetType(), "分页查询异常", ex); } return result; } } ``` ## 🔧 最佳实践 ### 1. 异常处理层次 ```csharp // 业务异常 - 用户可理解的错误 throw new BusinessException("用户名已存在,请选择其他用户名"); // 验证异常 - 输入格式错误 throw new ValidationException("邮箱格式不正确"); // 系统异常 - 记录详细日志,返回通用错误消息 Log4Helper.Error(GetType(), "数据库连接失败", ex); return RequestResult.Failed("系统异常,请联系管理员"); ``` ### 2. 日志记录规范 ```csharp // Info: 正常业务流程 Log4Helper.Info(GetType(), $"用户 {userName} 登录成功"); // Warning: 业务异常,需要关注但不影响系统运行 Log4Helper.Warning(GetType(), "用户尝试访问无权限资源", ex); // Error: 系统异常,需要立即处理 Log4Helper.Error(GetType(), "数据库操作失败", ex); ``` ### 3. 性能优化 ```csharp // ✅ 使用异步操作 await repository.GetListAsync(cancellationToken).ConfigureAwait(false); // ✅ 合理使用缓存 var cacheKey = $"user_{userId}"; var user = await _cache.GetOrAddAsync(cacheKey, () => _userRepository.GetAsync(userId), TimeSpan.FromMinutes(30)); // ✅ 批量操作 await repository.InsertRangeAsync(users, cancellationToken); ``` ### 4. 依赖注入 ```csharp // ✅ 构造函数注入 public class UserAppService : SOMSAppServiceBase { private readonly IUserRepository _userRepository; private readonly IUserDomainService _userDomainService; public UserAppService( IUserRepository userRepository, IUserDomainService userDomainService) { _userRepository = userRepository; _userDomainService = userDomainService; } } ``` ## ✅ 编译检查 - 每次编写代码后立即编译 - 解决所有编译错误和警告 - 使用 `dotnet build` 验证整个解决方案 - 启用 Nullable 引用类型检查 - 配置 EditorConfig 统一代码格式 ## 🚫 开发约束 - ❌ 不需要创建测试项目 - ❌ 不需要生成 Summary.md 文件 - ❌ 避免在Domain层引用基础设施 - ❌ 不要在AppService中直接操作数据库连接 - ❌ 避免在业务逻辑中硬编码字符串 ## 📚 参考文档 - [ASP.NET Boilerplate官方文档](https://aspnetboilerplate.com/Pages/Documents) - .NET 6.0 官方文档 - C# 编程指南