396 lines
11 KiB
Plaintext
396 lines
11 KiB
Plaintext
# .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<RequestResult<UserOutput>> 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<RequestResult<UserOutput>> GetUserAsync(
|
||
Guid userId,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var user = await _userRepository
|
||
.GetAsync(userId, cancellationToken)
|
||
.ConfigureAwait(false);
|
||
|
||
return new RequestResult<UserOutput>
|
||
{
|
||
Flag = true,
|
||
ResultData = ObjectMapper.Map<UserOutput>(user)
|
||
};
|
||
}
|
||
|
||
// ❌ 避免的写法
|
||
public RequestResult<UserOutput> GetUser(Guid userId) // 缺少async
|
||
{
|
||
var user = _userRepository.Get(userId); // 同步调用
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### C# 8.0+ 特性使用
|
||
|
||
```csharp
|
||
// ✅ Nullable引用类型
|
||
public async Task<RequestResult<string?>> GetOptionalDataAsync(Guid? id)
|
||
{
|
||
if (id is null) return RequestResult<string?>.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<RequestResult<UserOutput>> CreateUserAsync(
|
||
CreateUserInput input,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var result = new RequestResult<UserOutput>();
|
||
|
||
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<UserOutput>(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<TEntity, TDto, TKey, TCreateInput, TUpdateInput>
|
||
: SOMSAppServiceBase
|
||
where TEntity : class, IEntity<TKey>
|
||
where TDto : IEntityDto<TKey>
|
||
{
|
||
protected readonly IRepository<TEntity, TKey> Repository;
|
||
|
||
protected StandardCrudAppService(IRepository<TEntity, TKey> repository)
|
||
{
|
||
Repository = repository;
|
||
}
|
||
|
||
public virtual async Task<RequestResult<TDto>> GetAsync(
|
||
TKey id,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var result = new RequestResult<TDto>();
|
||
|
||
try
|
||
{
|
||
var entity = await Repository
|
||
.GetAsync(id, cancellationToken)
|
||
.ConfigureAwait(false);
|
||
|
||
result.Flag = true;
|
||
result.ResultData = ObjectMapper.Map<TDto>(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<RequestResult<PagedResultDto<TDto>>> GetPagedAsync(
|
||
PagedAndSortedRequestDto input,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var result = new RequestResult<PagedResultDto<TDto>>();
|
||
|
||
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<TDto>
|
||
{
|
||
TotalCount = totalCount,
|
||
Items = ObjectMapper.Map<List<TDto>>(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# 编程指南 |