RuoVea.ExDto
10.0.0.4
dotnet add package RuoVea.ExDto --version 10.0.0.4
NuGet\Install-Package RuoVea.ExDto -Version 10.0.0.4
<PackageReference Include="RuoVea.ExDto" Version="10.0.0.4" />
<PackageVersion Include="RuoVea.ExDto" Version="10.0.0.4" />
<PackageReference Include="RuoVea.ExDto" />
paket add RuoVea.ExDto --version 10.0.0.4
#r "nuget: RuoVea.ExDto, 10.0.0.4"
#:package RuoVea.ExDto@10.0.0.4
#addin nuget:?package=RuoVea.ExDto&version=10.0.0.4
#tool nuget:?package=RuoVea.ExDto&version=10.0.0.4
📦 RuoVea.ExDto
纯 BCL 共享契约库 — 提供 RESTful 响应模型、分页参数与结果、17 种业务枚举、实体基类接口、用户上下文抽象、Claim 常量及反射工具,内置 8 种语言的本地化资源,零 NuGet 依赖。
📖 目录
📋 概览
RuoVea.ExDto 为 .NET 企业级应用提供统一的 DTO 契约层,避免各层之间重复定义数据结构。纯 BCL 实现,零第三方依赖,可直接放入任何 .NET 8.0+ 项目中。
┌──────────────────────────────────────────────────────────┐
│ RuoVea.ExDto │
├──────────────────────────────────────────────────────────┤
│ RESTful 响应 分页模型 实体接口 │
│ ├─ RestfulResult ├─ PageParam ├─ IPrimaryKey │
│ ├─ RestfulResult<T> ├─ PageParam<T> ├─ IAuditable │
│ ├─ Restful (工厂) ├─ PageResult<T> ├─ IDeleted │
│ └─ Restful<T> (工厂) ├─ PageData ├─ IDataEntity │
│ └─ Pagination └─ ITenantEntity│
│ │
│ 枚举系统 (17种) Claim 常量 用户上下文 │
│ ├─ CodeStatus ├─ CLAINM_USERID ├─ ICurrentUser │
│ ├─ ErrorEnum (81项) ├─ CLAINM_ACCOUNT └─ ICurrentUser<TT>
│ ├─ DataOpType ├─ CLAINM_NAME
│ ├─ BusinessType ├─ TENANT_ID
│ ├─ QueryTypeEnum └─ ... (共14项)
│ └─ ... (共17种)
│ │
│ 反射工具 国际化 (8语言) │
│ ├─ EnumManager ├─ zh-CN / zh-TW / zh-HK │
│ └─ DisplayInfo ├─ en-US / fr-FR / ja-JP / vi-VN │
│ └─ I18nConverter (NET5.0+) │
├──────────────────────────────────────────────────────────┤
│ 命名空间: RuoVea.ExDto | RuoVea.ExEnum │
│ 零依赖 纯 BCL 共享契约库 │
└──────────────────────────────────────────────────────────┘
设计原则
| 原则 | 说明 |
|---|---|
| 零依赖 | 仅依赖 .NET BCL(System.Collections.Concurrent、System.ComponentModel.DataAnnotations 等),无任何第三方 NuGet 引用 |
| 纯数据契约 | 不包含业务逻辑,不注册 DI,所有类型可直接 new 使用 |
| 静态工厂 | Restful/Restful<T> 提供静态方法创建标准化响应,避免分散的 new RestfulResult { ... } |
| 高性能缓存 | EnumManager 使用 ConcurrentDictionary 作为缓存层,反射结果只计算一次 |
| 多语言支持 | 8 种 resx 资源文件覆盖中、英、法、日、越南语及粤语地区 |
📦 安装
.NET CLI
# .NET 8.0
dotnet add package RuoVea.ExDto --version 8.0.1.5
# .NET 10.0
dotnet add package RuoVea.ExDto --version 10.0.0.3
Package Manager
Install-Package RuoVea.ExDto -Version 8.0.1.5
PackageReference
<PackageReference Include="RuoVea.ExDto" Version="8.0.1.5" />
支持的 Target Framework
| TFM | 最低版本 |
|---|---|
net8.0 |
8.0.1.5 |
net10.0 |
10.0.0.3 |
⚡ 30 秒快速开始
1. 导入命名空间
using RuoVea.ExDto; // 模型、接口、工具类
using RuoVea.ExEnum; // 所有枚举类型
2. 第一行代码
// <inheritdoc cref="Restful{T}.Success"/>
// 创建成功响应
var successResult = Restful<string>.Success("Hello, ExDto!");
// <inheritdoc cref="Restful.Bad(string)"/>
// 创建错误响应
var errorResult = Restful.Bad("参数校验失败");
// <inheritdoc cref="PageParam"/>
// 分页查询参数
var pageParam = new PageParam(pageNo: 1, pageSize: 20)
{
Sidx = "CreateTime",
Sord = "desc"
};
// <inheritdoc cref="PageResult{T}"/>
// 分页结果
var pageResult = new PageResult<string>(
rows: new[] { "A", "B", "C" },
pageNo: 1,
pageSize: 20,
totalRows: 100
);
Console.WriteLine($"共 {pageResult.TotalPage} 页,{pageResult.TotalRows} 条记录");
3. 使用枚举和反射工具
// <inheritdoc cref="EnumManager.GetEnumItems{T}"/>
var adminTypes = EnumManager.GetEnumItems<AdminType>();
// → [{ Key: "None", Value: "0" }, { Key: "Admin", Value: "1" }, { Key: "SuperAdmin", Value: "2" }]
// <inheritdoc cref="EnumManager.GetAllEnumTypesInfo"/>
var allEnums = EnumManager.GetAllEnumTypesInfo();
// → 返回当前程序集中所有 17 种枚举的结构化信息
// <inheritdoc cref="ClaimConst.GetAllConstantInfos"/>
var claimInfo = ClaimConst.GetAllConstantInfos();
// → 14 个 Claim 常量的结构化元数据
30 秒内你完成了: RESTful 成功/失败响应 → 分页参数构建 → 枚举反射查询 → Claim 常量读取。无需任何 DI 注册或配置。
🧩 核心场景
场景一:RESTful 响应模型
┌────────────┐ Restful<T>.Success(data) ┌──────────────────────┐
│ 业务数据 │ ──────────────────────────▶ │ RestfulResult<T> │
│ (T data) │ │ ├─ Code: OK (200) │
└────────────┘ │ ├─ Data: <T> │
│ ├─ Message: string │
┌────────────┐ Restful.Bad(message) │ ├─ Extras: object │
│ 错误信息 │ ──────────────────────────▶ │ └─ Timestamp: long │
└────────────┘ └──────────────────────┘
泛型响应
// <inheritdoc cref="Restful{T}.Success(T)"/>
var result = Restful<UserDto>.Success(new UserDto { Id = 1, Name = "张三" });
// result.Code == CodeStatus.OK
// result.Data is UserDto
// <inheritdoc cref="Restful{T}.Bad(string)"/>
var error = Restful<UserDto>.Bad("用户名已存在");
// error.Code == CodeStatus.BadRequest (400)
// error.Message == "用户名已存在"
// error.Data == null
// <inheritdoc cref="Restful{T}.Bad(CodeStatus, string)"/>
// 自定义状态码
var forbidden = Restful<UserDto>.Bad(CodeStatus.Forbidden, "无权访问该资源");
非泛型响应
// <inheritdoc cref="Restful.Success(object)"/>
var result = Restful.Success(new { Total = 100, Items = new[] { 1, 2, 3 } });
// <inheritdoc cref="Restful.Bad(string)"/>
var error = Restful.Bad("操作失败");
附加数据 (Extras)
// <inheritdoc cref="RestfulResult.Extras"/>
var result = Restful<UserDto>.Success(user);
result.Extras = new { Token = "jwt-token-string", ExpiresIn = 7200 };
// 前端可一并获取 token 和业务数据
✅ 已修复:
RestfulResult.Timestamp已改为DateTimeOffset.UtcNow.ToUnixTimeSeconds(),输出真正的 Unix 时间戳(UTC 秒),跨时区一致。
场景二:分页参数与结果
┌─────────────────┐ ┌──────────────────────┐
│ PageParam │ 传入 Service 层 │ PageResult<T> │
│ ├─ PageNo: 1 │ ────────────────▶ │ ├─ PageNo: 1 │
│ ├─ PageSize: 20 │ │ ├─ PageSize: 20 │
│ ├─ Sidx: "Id" │ │ ├─ TotalRows: 100 │
│ ├─ Sord: "desc" │ │ ├─ TotalPage: 5 │
│ └─ BeginTime │ │ └─ Rows: IEnumerable │
└─────────────────┘ └──────────────────────┘
// <inheritdoc cref="PageParam"/>
// 基础分页参数
var param = new PageParam
{
PageNo = 2,
PageSize = 50,
Sidx = "CreateTime",
Sord = "desc",
BeginTime = DateTime.Now.AddDays(-30),
EndTime = DateTime.Now
};
// <inheritdoc cref="PageParam{T}"/>
// 带查询条件的分页参数
var paramWithFilter = new PageParam<OrderFilter>
{
PageNo = 1,
Filter = new OrderFilter { Status = "pending", MinAmount = 100 }
};
// <inheritdoc cref="PageResult{T}"/>
// 构建分页结果
var rows = await db.Orders
.Where(o => o.Status == "pending")
.OrderByDescending(o => o.CreateTime)
.Skip((param.PageNo - 1) * param.PageSize)
.Take(param.PageSize)
.ToListAsync();
var total = await db.Orders.CountAsync(o => o.Status == "pending");
var pageResult = new PageResult<OrderDto>(rows, param.PageNo, param.PageSize, total);
// pageResult.TotalPage 自动计算 (向上取整)
分页模型对比
| 类型 | 泛型 | 用途 | 典型场景 |
|---|---|---|---|
PageParam |
否 | 基础分页查询参数 | 简单列表查询 |
PageParam<T> |
是 | 带查询条件的分页参数 | 带 Filter 的高级搜索 |
PageResult<T> |
是 | 泛型分页结果 | 返回强类型列表 |
PageData |
否 | object 类型分页结果 |
兼容旧代码或动态数据 |
Pagination |
否 | 精简分页模型 | 内部计算或轻量传输 |
// <inheritdoc cref="PageData"/>
// 非泛型分页(用于兼容旧接口)
var pageData = new PageData(new { Items = dataList }, 1, 20, 100);
// <inheritdoc cref="Pagination"/>
// 精简分页(TotalPage 基于 Total 自动计算)
var pagination = new Pagination(page: 2, pageSize: 30) { Total = 100 };
Console.WriteLine(pagination.TotalPage); // 4 (100/30 = 3.33 → 向上取整)
❗ TotalPage 计算规则:
PageResult<T>、PageData、Pagination的TotalPage均采用TotalRows % PageSize == 0 ? TotalRows / PageSize : TotalRows / PageSize + 1的向上取整逻辑。当TotalRows <= 0时返回0。
场景三:实体基类接口
┌──────────────────────────────────────────────────────┐
│ IDataEntity │
│ ├─ IPrimaryKeyEntity: long Id │
│ ├─ IAuditableEntity: CreateTime, Creator, │
│ │ ModifyTime, Modifier │
│ └─ IDeletedEntity: IsDelete? (逻辑删除) │
├──────────────────────────────────────────────────────┤
│ ITenantEntity: long? TenantId (多租户) │
└──────────────────────────────────────────────────────┘
using RuoVea.ExDto;
using RuoVea.ExEnum;
// <inheritdoc cref="IDataEntity"/>
// 完整的审计实体(主键 + 审计 + 逻辑删除)
public class UserEntity : IDataEntity, ITenantEntity
{
public long Id { get; set; }
public DateTime? CreateTime { get; set; }
public long? Creator { get; set; }
public DateTime? ModifyTime { get; set; }
public long? Modifier { get; set; }
public IsDelete? IsDelete { get; set; }
public long? TenantId { get; set; }
// 业务字段
public string Name { get; set; }
public string Email { get; set; }
}
// <inheritdoc cref="IAuditableEntity"/>
// 仅审计字段(无主键、无逻辑删除),适用于关联表
public class UserRoleEntity : IAuditableEntity
{
public long UserId { get; set; }
public long RoleId { get; set; }
public DateTime? CreateTime { get; set; }
public long? Creator { get; set; }
public DateTime? ModifyTime { get; set; }
public long? Modifier { get; set; }
}
// 通用仓储约束
public interface IRepository<T> where T : IDataEntity
{
Task<T> GetByIdAsync(long id);
Task<PageResult<T>> GetPagedAsync(PageParam param);
Task SoftDeleteAsync(long id); // 设置 IsDelete = IsDelete.Y
}
实体接口详解
| 接口 | 成员 | 说明 |
|---|---|---|
IPrimaryKeyEntity |
long Id |
统一长整型主键约定 |
IAuditableEntity |
DateTime? CreateTime, long? Creator, DateTime? ModifyTime, long? Modifier |
创建/修改审计四个字段 |
IDeletedEntity |
IsDelete? IsDelete |
逻辑删除标记(IsDelete.N=未删除, IsDelete.Y=已删除) |
IDataEntity |
继承以上三者 | 完整的数据实体契约 |
ITenantEntity |
long? TenantId |
多租户隔离标识 |
场景四:用户上下文抽象
using RuoVea.ExDto;
// <inheritdoc cref="ICurrentUser"/>
// 标准 long 型用户上下文
public class AppUser : ICurrentUser
{
public long UserId { get; set; }
public long TenantId { get; set; }
public string Account { get; set; }
public string Name { get; set; }
public bool IsSuperAdmin { get; set; }
public bool IsTenantAdmin { get; set; }
public T User<T>() where T : class
{
// 返回完整用户实体(可配合 EF Core 查询)
return this as T;
}
}
// <inheritdoc cref="ICurrentUser{TT}"/>
// 泛型用户上下文(支持 string / Guid 主键)
public class AppUser<TT> : ICurrentUser<TT>
{
public TT UserId { get; set; }
public TT TenantId { get; set; }
public string Account { get; set; }
public string Name { get; set; }
public bool IsSuperAdmin { get; set; }
public bool IsTenantAdmin { get; set; }
public T User<T>() where T : class => null;
}
// Controller 中使用
[ApiController]
public class OrderController : ControllerBase
{
private readonly ICurrentUser _currentUser;
public OrderController(ICurrentUser currentUser)
{
_currentUser = currentUser;
}
[HttpGet]
public IActionResult GetOrders()
{
// 利用用户上下文进行数据隔离
if (!_currentUser.IsSuperAdmin)
{
// 非超级管理员仅查询本租户数据
return Ok(await _orderService.GetByTenantAsync(_currentUser.TenantId));
}
return Ok(await _orderService.GetAllAsync());
}
}
场景五:枚举与常量管理
┌──────────────┐ EnumManager.GetEnumItems<T>() ┌─────────────────┐
│ 枚举类型 T │ ─────────────────────────────────▶ │ List<DisplayInfo>│
│ (17种枚举) │ EnumManager.GetEnumDictionary<T>()│ (带缓存) │
└──────────────┘ └─────────────────┘
获取单个枚举项
// <inheritdoc cref="EnumManager.GetEnumItems{T}"/>
var items = EnumManager.GetEnumItems<StatusEnum>();
// → [
// { Key: "ENABLE", Value: "0", Name: "0", Description: "启用" },
// { Key: "DISABLE", Value: "1", Name: "1", Description: "停用" }
// ]
// <inheritdoc cref="EnumManager.GetEnumDictionary{T}"/>
var dict = EnumManager.GetEnumDictionary<Gender>();
// → { 1: "男", 2: "女", 3: "未知" }
批量获取与全局查询
// <inheritdoc cref="EnumManager.GetMultipleEnums"/>
var enums = EnumManager.GetMultipleEnums(typeof(StatusEnum), typeof(YesOrNot), typeof(Gender));
// <inheritdoc cref="EnumManager.GetAllEnumTypesInfo"/>
// 获取程序集中所有 17 种枚举的完整层级信息(含子项)
var allEnumInfo = EnumManager.GetAllEnumTypesInfo();
foreach (var enumInfo in allEnumInfo)
{
Console.WriteLine($"{enumInfo.Name}: {enumInfo.Children.Count} 个值");
}
// <inheritdoc cref="EnumManager.GetSpecifiedEnumInfo"/>
var codeStatusInfo = EnumManager.GetSpecifiedEnumInfo("CodeStatus");
// → 包含 40 个 HTTP 状态码枚举值
// <inheritdoc cref="EnumManager.ClearEnumTypesCache"/>
// 若运行时动态加载了新枚举,可清除缓存强制重新反射
EnumManager.ClearEnumTypesCache();
❗ 性能提示:
EnumManager使用ConcurrentDictionary进行三层缓存。首次调用触发反射,后续直接返回缓存。新增ClearAllCaches()可一次性清除全部三层缓存;ClearEnumTypesCache()仅清除类型信息缓存。
场景六:Claim 常量
// <inheritdoc cref="ClaimConst"/>
// 14 个标准 Claim Key 常量,避免硬编码字符串
var userIdClaim = ClaimConst.CLAINM_USERID; // "UserId"
var accountClaim = ClaimConst.CLAINM_ACCOUNT; // "Account"
var tenantIdClaim = ClaimConst.TENANT_ID; // "TenantId"
var openIdClaim = ClaimConst.OpenId; // "OpenId"
// JWT Token 生成时使用
var claims = new[]
{
new Claim(ClaimConst.CLAINM_USERID, user.Id.ToString()),
new Claim(ClaimConst.CLAINM_ACCOUNT, user.Account),
new Claim(ClaimConst.CLAINM_NAME, user.Name),
new Claim(ClaimConst.TENANT_ID, user.TenantId?.ToString() ?? ""),
new Claim(ClaimConst.CLAINM_SUPERADMIN, user.IsSuperAdmin.ToString()),
new Claim(ClaimConst.LoginMode, "PC")
};
// <inheritdoc cref="ClaimConst.GetAllConstantInfos"/>
var allClaimInfo = ClaimConst.GetAllConstantInfos();
// → 返回 DisplayInfo 树形结构,包含 14 个子项,每项含 Name/ShortName/Description/GroupName/Order
foreach (var child in allClaimInfo.Children)
{
Console.WriteLine($"{child.Name} = \"{child.Value}\" ({child.Description})");
}
Claim 常量分组
| 分组 | Claim Key | 常量值 | 说明 |
|---|---|---|---|
| UserInfo | CLAIINM_USERID |
"UserId" |
用户ID |
CLAIINM_ACCOUNT |
"Account" |
账号 | |
CLAIINM_NAME |
"Name" |
名称 | |
RealName |
"RealName" |
真实姓名 | |
NickName |
"NickName" |
昵称 | |
AccountType |
"AccountType" |
账号类型 | |
| RolePermission | CLAIINM_ROLEIDS |
"RoleIds" |
角色Id |
CLAIINM_ISADMIN |
"IsAdmin" |
是否管理 | |
CLAIINM_SUPERADMIN |
"SuperAdmin" |
是否超级管理 | |
| Organization | TENANT_ID |
"TenantId" |
租户Id |
OrgId |
"OrgId" |
组织机构Id | |
OrgName |
"OrgName" |
组织机构名称 | |
| Authentication | OpenId |
"OpenId" |
微信OpenId |
LoginMode |
"LoginMode" |
登录模式(PC/APP) |
⚙️ 配置选项详解
本包是纯数据契约库,无 DI 扩展方法,所有类型均可直接实例化,无需任何配置。
RestfulResult / RestfulResult<T>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Code |
CodeStatus |
OK (200) |
HTTP 状态码枚举 |
Message |
object / string |
null |
错误/提示信息 |
Data |
object / T |
null |
响应数据 |
Extras |
object |
null |
附加数据(如 Token、分页信息) |
Timestamp |
long |
Unix 秒(UTC) | DateTimeOffset.UtcNow.ToUnixTimeSeconds() |
Restful / Restful<T> 工厂方法
| 方法 | 返回 | 说明 |
|---|---|---|
Success() |
RestfulResult / RestfulResult<T> |
Code = OK, Message/Data = null |
Success(T data) / Success(object data) |
同上 | Code = OK, Data = data |
Bad() |
同上 | Code = BadRequest |
Bad(string message) |
同上 | Code = BadRequest, Message = message |
Bad(CodeStatus code, string message) |
同上 | 自定义 Code 和 Message |
PageParam / PageParam<T>
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
PageNo |
int |
1 |
当前页码(从1开始) |
PageSize |
int |
20 |
每页行数 |
Sidx |
string |
null |
排序列名称 |
Sord |
string |
"asc" |
排序方向 ("asc" / "desc") |
BeginTime |
DateTime? |
null |
搜索开始时间 |
EndTime |
DateTime? |
null |
搜索结束时间 |
Filter (PageParam<T> only) |
T |
default |
查询条件对象 |
IRESTfulResult 接口
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
RESTfulResult |
CodeStatus statusCode, object data, object message, object extras |
dynamic |
统一 RESTful 响应方法签名 |
❗ 命名警告:
IRESTfulResult接口定义在文件IRestfulFilterLog.cs中,接口名与文件名不匹配。查找该接口时请搜索IRESTfulResult而非IRestfulFilterLog。
DisplayInfo 模型
| 属性 | 类型 | 说明 |
|---|---|---|
Value |
string |
常量/枚举的数值(字符串形式) |
Name |
string |
显示名称 |
ShortName |
string |
短名称/简称 |
Description |
string |
描述文本 |
GroupName |
string |
分组名称 |
Order |
int |
排序顺序 |
Key |
string |
常量/枚举键名 |
Children |
List<DisplayInfo> |
子项列表(用于树形结构) |
I18nConverter(条件编译)
// 仅在 NET5.0_OR_GREATER 下可用
#if NET5_0_OR_GREATER
// <inheritdoc cref="I18nConverter.ConvertResxToJson"/>
// 将 resx 资源转换为 JSON 字符串
string json = I18nConverter.ConvertResxToJson();
// → { "continue": "Continue", "badrequest": "Bad Request", ... }
#endif
🧵 线程安全
| 组件 | 线程安全 | 说明 |
|---|---|---|
EnumManager |
✅ 是 | 所有缓存使用 ConcurrentDictionary.GetOrAdd,确保只反射一次 |
ClaimConst.GetAllConstantInfos() |
✅ 是 | 使用 ConcurrentDictionary.GetOrAdd |
Restful / Restful<T> |
✅ 是 | 纯静态工厂,每次返回新实例 |
RestfulResult / RestfulResult<T> |
✅ 是 | 普通 POCO 类,无共享状态 |
PageParam / PageResult<T> / PageData / Pagination |
✅ 是 | 普通 POCO 类,无共享状态 |
DisplayInfo |
✅ 是 | 普通 POCO 类,无共享状态 |
i18n / I18nConverter |
✅ 是 | ResourceManager 天然线程安全 |
实体接口 (IDataEntity 等) |
✅ 是 | 无状态的接口定义 |
| 枚举类型 | ✅ 是 | 不可变值类型 |
✅ 全部线程安全。本包是纯数据契约库,所有类型均无共享可变状态。
📊 类型速查表
17 种枚举速查
| 枚举 | 值数量 | 典型值 |
|---|---|---|
AdminType |
3 | None=0, Admin=1, SuperAdmin=2 |
BusinessType |
10 | OTHER, INSERT, UPDATE, DELETE, GRANT, EXPORT, IMPORT, FORCE, GENCODE, CLEAN |
CategoryEnum |
11 | NORMAL, GITEE, GITHUB, WECHART, AILIPAY, QQ, BAIDU, TRILl, DingTalkQrcodeOAuth, Microsoft, Coding |
CodeStatus |
40 | HTTP 100–505,含 OK=200, BadRequest=400, Unauthorized=401, Forbidden=403, NotFound=404, InternalServerError=500 |
DataOpType |
14 | OTHER, ADD, DELETE, EDIT, UPDATE, QUERY, DETAIL, TREE, IMPORT, EXPORT, GRANT, FORCE, CLEAN, CHANGE_STATUS |
DataScopeType |
5 | ALL=1, DEPT_WITH_CHILD=2, DEPT=3, SELF=4, DEFINE=5 |
ErrorEnum |
81 | D1000–D1600, xg1000–xg1002, B1000–B1006 系统错误码 |
Gender |
3 | MALE=1, FEMALE=2, UNKNOWN=3 |
IsDelete |
2 | N=0, Y=1 |
LoginType |
5 | LOGIN, LOGOUT, REGISTER, CHANGEPASSWORD, AUTHORIZEDLOGIN |
MenuType |
3 | DIR=0, MENU=1, BTN=2 |
QueryTypeEnum |
8 | eq=0, like=1, gt=2, lt=3, ne=4, ge=5, le=6, isNotNull=7 |
RequestTypeEnum |
5 | Run=0, Get=1, Post=2, Put=3, Delete=4 |
SordEnum |
2 | Asc=1, Desc=2 |
StatusEnum |
2 | ENABLE=0, DISABLE=1 |
TenantTypeEnum |
2 | COMMON=0, SYSTEM=1 |
YesOrNot |
2 | N=0, Y=1 |
模型类型速查
| 类型 | 用途 | 关键成员 |
|---|---|---|
RestfulResult |
非泛型 RESTful 响应 | Code, Message, Data, Extras, Timestamp |
RestfulResult<T> |
泛型 RESTful 响应 | 同上 + 泛型 Data: T |
Restful |
非泛型静态工厂 | Success(), Success(object), Bad(), Bad(string), Bad(CodeStatus, string) |
Restful<T> |
泛型静态工厂 | 同上,返回 RestfulResult<T> |
PageParam |
基础分页查询参数 | PageNo=1, PageSize=20, Sidx, Sord="asc", BeginTime, EndTime |
PageParam<T> |
带条件的分页参数 | 继承 PageParam + Filter: T |
PageResult<T> |
泛型分页结果 | PageNo, PageSize, TotalPage(计算), TotalRows, Rows |
PageData |
非泛型分页结果 | 同上,Rows: object |
Pagination |
精简分页模型 | PageSize, PageNo, Sidx, Sord, Total, TotalPage(计算) |
接口类型速查
| 接口 | 用途 | 关键成员 |
|---|---|---|
IPrimaryKeyEntity |
主键约定 | long Id |
IAuditableEntity |
审计字段约定 | CreateTime, Creator, ModifyTime, Modifier |
IDeletedEntity |
逻辑删除约定 | IsDelete? |
IDataEntity |
完整数据实体契约 | 继承 IPrimaryKeyEntity + IAuditableEntity + IDeletedEntity |
ITenantEntity |
多租户隔离 | long? TenantId |
ICurrentUser<TT> |
泛型用户上下文 | TT UserId, TT TenantId, Account, Name, IsSuperAdmin, IsTenantAdmin, User<T>() |
ICurrentUser |
标准用户上下文 | 继承 ICurrentUser<long> |
IRESTfulResult |
RESTful 响应约定 | dynamic RESTfulResult(CodeStatus, object data, object message, object extras) |
🌍 国际化
i18n 资源类(RuoVea.ExDto.Language 命名空间)提供 8 种语言的本地化字符串,根据 CultureInfo.CurrentUICulture 自动切换:
| 语言 | 区域代码 | 资源文件 |
|---|---|---|
| 简体中文 (默认) | zh-CN |
i18n.resx |
| 繁体中文(台湾) | zh-TW |
i18n.zh-TW.resx |
| 粤语(香港) | zh-HK |
i18n.zh-HK.resx |
| 英语 | en-US |
i18n.en-US.resx |
| 法语 | fr-FR |
i18n.fr-FR.resx |
| 日语 | ja-JP |
i18n.ja-JP.resx |
| 越南语 | vi-VN |
i18n.vi-VN.resx |
注:
i18n.resx为默认回退资源(简体中文),其余按 CultureInfo 自动匹配。
using RuoVea.ExDto.Language;
// 当前线程 UI 文化为 en-US 时:
// i18n.@continue → "Continue"
// i18n.badrequest → "Bad Request"
// i18n.notfound → "Not Found"
// 当前线程 UI 文化为 ja-JP 时对应的日语本地化字符串
I18nConverter(NET5.0+ 专属)
#if NET5_0_OR_GREATER
// <inheritdoc cref="I18nConverter.ConvertResxToJson"/>
// 将当前语言的 resx 资源导出为 JSON 字符串
// 可用于前端 i18n 初始化或 API 输出
string json = I18nConverter.ConvertResxToJson();
#endif
⚠️ 关键警告
1. RestfulResult.Timestamp 时区硬编码
// RestfulResult.cs 原始实现:
public long Timestamp { get; set; } =
(DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000 - 8 * 60 * 60;
✅ 已修复:
Timestamp现使用DateTimeOffset.UtcNow.ToUnixTimeSeconds()生成标准 Unix 时间戳,跨时区一致。
2. 无 DI 注册扩展
本包是纯数据库,不包含 IServiceCollection 扩展方法。DI 注册需由调用方自行处理:
// 需自行注册 ICurrentUser 的实现
services.AddScoped<ICurrentUser, AppUser>();
3. IRESTfulResult 接口文件名不匹配
接口 IRESTfulResult 定义在 IRestfulFilterLog.cs 文件中,两者名称不一致。通过 IDE 搜索时应使用接口名 IRESTfulResult 而非文件名。
4. PageData 非泛型限制
PageData.Rows 类型为 object,使用时需要手动类型转换。新项目建议优先使用 PageResult<T>。
5. EnumManager 程序集扫描范围
GetAllEnumTypesInfo() 仅扫描命名空间为 RuoVea.ExEnum 的枚举类型。若在外部程序集定义了同命名空间的枚举,不会被自动扫描到——需使用 GetAllEnums(assemblies) 重载。
🗺️ 版本迁移指南
从手工定义 DTO 迁移
| 旧代码模式 | 迁移到 RuoVea.ExDto |
|---|---|
自定义 ApiResult<T> 类 |
RestfulResult<T> + Restful<T>.Success() / Restful<T>.Bad() |
自定义 PagedList<T> 类 |
PageResult<T> |
手动定义 long Id 接口 |
IPrimaryKeyEntity |
手动定义 CreateTime/Creator 审计字段 |
IAuditableEntity |
const string UserId = "UserId" 硬编码 |
ClaimConst.CLAINM_USERID |
手动 Enum.GetValues() + GetName() |
EnumManager.GetEnumItems<T>() |
v8.0.x → v10.0.x
- API 无变化。v10.0.x 仅在
net10.0TFM 上编译,所有公开 API 签名与 v8.0.x 保持一致。 - 无依赖包变更。
📄 License
MIT License © RuoVea
🔗 相关资源: NuGet Gallery · 问题反馈
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (7)
Showing the top 5 NuGet packages that depend on RuoVea.ExDto:
| Package | Downloads |
|---|---|
|
RuoVea.ExSugar
Sqlsugar扩展 快速注入,支持简体中文、繁体中文、粤语、日语、法语、英语.使用方式:service.AddSqlsugar();继承RestFulLog 重写异常日志,操作日志,差异日志 |
|
|
RuoVea.ExFilter
注入 进行全局的异常日志收集、执行操作日志、参数验证,支持简体中文、繁体中文、粤语、日语、法语、英语.services.ExceptionSetup();// 注入 全局错误日志处 services.ExceptionSetup(ExceptionLog actionOptions);// 注入 全局错误日志处 services.ExceptionSetup(builder.Configuration.GetSection("AopOption:ExceptionLog"));// 注入 全局错误日志处 services.RequestActionSetup();// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.RequestActionSetup(RequestLog actionOptions);// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.RequestActionSetup(builder.Configuration.GetSection("AopOption:RequestLog"));// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.ResourceSetup();//对资源型信息进行过滤 services.ResultSetup();//对结果进行统一 services.ApISafeSetup(AppSign actionOptions);//接口安全校验 services.ApISafeSetup(builder.Configuration.GetSection("AopOption:AppSign"));//接口安全校验 services.ApISignSetup(AppSign actionOptions);//签名验证 ( appKey + signKey + timeStamp + data ); services.ApISignSetup(builder.Configuration.GetSection("AopOption:AppSign"));//签名验证 ( appKey + signKey + timeStamp + data ); services.AddValidateSetup();//模型校验 services.AddUiFilesZipSetup();//将前端UI压缩文件进行解压 不进行接口安全校验 -> NonAplSafeAttribute 不签名验证 -> NonAplSignAttribute 不进行全局的异常日志收集 -> NonExceptionAttribute 不对资源型信息进行过滤 -> NonResourceAttribute 不对结果进行统一 -> NonRestfulResultAttribute |
|
|
RuoVea.ExJwtBearer
Jwt 授权验证拓展插件。声名:IJwtHelper _jwtHelper,支持简体中文、繁体中文、粤语、日语、法语、英语.添加验权:services.AddAuthenticationSetup(enableGlobalAuthorize: true);添加鉴权:services.AddAuthorizationSetup.MyPermission.(enableGlobalAuthorize: true);添加Jwt加密:services.AddJwtSetup(); |
|
|
RuoVea.ExWeb
CorsUrls、IPLimit、SafeIps、Jwt 配置 |
|
|
RuoVea.ExGlobal
web 注入 全局错误日志、操作日志记录 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.0.4 | 2,534 | 6/24/2026 |
| 10.0.0.3 | 1,132 | 1/26/2026 |
| 10.0.0.2 | 200 | 1/12/2026 |
| 9.0.0.4 | 1,374 | 1/26/2026 |
| 9.0.0.3 | 440 | 10/23/2025 |
| 9.0.0.2 | 283 | 10/23/2025 |
| 8.0.1.6 | 4,511 | 6/24/2026 |
| 8.0.1.5 | 2,689 | 1/26/2026 |
| 8.0.1.4 | 3,558 | 10/23/2025 |
| 8.0.1.3 | 285 | 10/23/2025 |
| 7.0.1.5 | 2,491 | 1/26/2026 |
| 7.0.1.4 | 4,140 | 10/23/2025 |
| 6.0.12.5 | 3,010 | 1/26/2026 |
| 6.0.12.4 | 5,247 | 10/23/2025 |
| 5.0.16.5 | 511 | 1/26/2026 |
| 5.0.16.4 | 500 | 10/23/2025 |
| 2.1.1.5 | 155 | 1/26/2026 |
| 2.1.1.4 | 274 | 10/23/2025 |
| 2.0.0.5 | 144 | 1/26/2026 |
| 2.0.0.4 | 286 | 10/23/2025 |